usePay.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584
  1. import {
  2. ref,
  3. reactive
  4. } from "vue";
  5. import {
  6. orderPayAli,
  7. orderPayWx,
  8. orderCheck,
  9. orderPayApple
  10. } from "@/api/shop";
  11. import {
  12. toast
  13. } from "@/utils/common";
  14. export function usePay(opt = {}) {
  15. const {
  16. createOrderError, // 创建订单失败
  17. checkError, // 校验失败
  18. payError, // 支付失败
  19. paySuccess, //支付成功
  20. applePayError, // 苹果内购失败
  21. } = opt;
  22. const aliData = reactive({
  23. orderId: null, // 订单编号
  24. text: '', // 订单数据
  25. })
  26. const wxData = reactive({
  27. appid: '', // 应用ID(AppID)
  28. mchid: '',
  29. noncestr: '', // 随机字符串
  30. orderId: '',
  31. packageVal: '',
  32. partnerid: '', // 商户号(PartnerID)
  33. pid: '',
  34. prepayid: '', // 预支付交易会话ID
  35. timestamp: '', // 时间戳(单位:秒)
  36. sign: '', // 签名,这里用的 MD5 签名
  37. package: '', // 固定值
  38. })
  39. const appleData = reactive({
  40. chanpinId: null, // 产品ID
  41. paynum: "", // 支付号
  42. receipt: "", // 票据
  43. taocanId: null, // 套餐ID
  44. })
  45. function resetStatus() {
  46. Object.assign(aliData, {
  47. orderId: null, // 订单编号
  48. text: '', // 订单数据
  49. })
  50. Object.assign(wxData, {
  51. appid: '', // 应用ID(AppID)
  52. mchid: '',
  53. noncestr: '', // 随机字符串
  54. orderId: '',
  55. packageVal: '',
  56. partnerid: '', // 商户号(PartnerID)
  57. pid: '',
  58. prepayid: '', // 预支付交易会话ID
  59. timestamp: '', // 时间戳(单位:秒)
  60. sign: '', // 签名,这里用的 MD5 签名
  61. package: '', // 固定值
  62. })
  63. Object.assign(appleData, {
  64. chanpinId: null, // 产品ID
  65. paynum: "", // 支付号
  66. receipt: "", // 票据
  67. taocanId: null, // 套餐ID
  68. })
  69. }
  70. // 支付入口 type:业务类型:wx|ali|apple; options: 入参chanpinId,taocanId,applePid
  71. function OrderPay(type, options) {
  72. // 初始化支付状态
  73. resetStatus();
  74. const {
  75. chanpinId,
  76. taocanId,
  77. applePid, // 苹果商品Id ---> 用来获取支付号+票据
  78. } = options;
  79. if (!chanpinId) {
  80. toast('数据异常,产品信息丢失')
  81. return;
  82. }
  83. if (!taocanId) {
  84. toast('数据异常,套餐信息丢失')
  85. return;
  86. }
  87. if (type == 'wx') {
  88. uni.showLoading({
  89. title: '订单创建中...',
  90. mask: true
  91. });
  92. // 微信
  93. orderPayWx({
  94. chanpinId,
  95. taocanId
  96. }).then(res => {
  97. uni.hideLoading();
  98. if (res.code == 0) {
  99. Object.assign(wxData, res.data);
  100. // 开始支付
  101. wxPay(res.data);
  102. } else {
  103. toast('微信订单创建失败')
  104. // 业务异常
  105. createOrderError && createOrderError({
  106. type: 'CreateOrderError',
  107. msg: '业务异常,创建订单返回状态异常',
  108. err: new Error('业务异常,创建订单返回状态异常'),
  109. form: 'wx'
  110. })
  111. }
  112. }).catch(err => {
  113. uni.hideLoading();
  114. if (typeof !isNaN(err) && err === 'number') {
  115. toast('创建微信订单失败')
  116. }
  117. createOrderError && createOrderError({
  118. type: 'CreateOrderError',
  119. msg: '创建微信订单失败',
  120. err,
  121. form: 'wx'
  122. })
  123. })
  124. }
  125. if (type == 'ali') {
  126. uni.showLoading({
  127. title: '订单创建中...',
  128. mask: true
  129. });
  130. // 支付宝
  131. orderPayAli({
  132. chanpinId,
  133. taocanId
  134. }).then(res => {
  135. uni.hideLoading();
  136. if (res.code == 0) {
  137. Object.assign(aliData, res.data);
  138. // 开始支付
  139. aliPay(res.data);
  140. } else {
  141. toast('支付宝订单创建失败')
  142. // 业务异常
  143. createOrderError && createOrderError({
  144. type: 'CreateOrderError',
  145. msg: '业务异常,创建订单返回状态异常',
  146. err: new Error('业务异常,创建订单返回状态异常'),
  147. form: 'ali'
  148. })
  149. }
  150. }).catch(err => {
  151. uni.hideLoading();
  152. if (typeof !isNaN(err) && err === 'number') {
  153. toast('创建支付宝订单失败')
  154. }
  155. createOrderError && createOrderError({
  156. type: 'CreateOrderError',
  157. msg: '创建支付宝订单失败',
  158. err,
  159. form: 'ali'
  160. })
  161. })
  162. }
  163. if (type == 'apple') {
  164. if (!applePid) {
  165. toast('数据异常,商品信息丢失')
  166. return;
  167. }
  168. uni.showLoading({
  169. title: '支付中请等待...',
  170. mask: true
  171. });
  172. applePay(options)
  173. }
  174. }
  175. // 微信支付
  176. function wxPay(options) {
  177. Object.assign(wxData, options);
  178. console.log('cccc', options)
  179. try {
  180. uni.requestPayment({
  181. provider: "wxpay",
  182. orderInfo: {
  183. "appid": options.appid,
  184. "partnerid": options.partnerId,
  185. "prepayid": options.prepayId,
  186. "package": options.packageVal,
  187. "noncestr": options.nonceStr,
  188. "timestamp": options.timestamp,
  189. "sign": options.sign ,
  190. },
  191. success: (res) => {
  192. uni.showLoading({
  193. title: '订单支付中...',
  194. mask: true
  195. });
  196. // 校验支付结果
  197. setTimeout(() => OrderCheckWx(), 1000)
  198. },
  199. fail: (err) => {
  200. if (err.errMsg) {
  201. toast(err.errMsg)
  202. } else {
  203. toast('微信支付失败,请联系管理员')
  204. }
  205. payError && payError({
  206. type: 'wxPay',
  207. msg: '微信支付失败',
  208. err,
  209. from: 'uni.requestPayment.fail'
  210. })
  211. }
  212. })
  213. } catch (err) {
  214. if (typeof !isNaN(err) && err === 'number') {
  215. toast('微信支付环境检测异常')
  216. }
  217. payError && payError({
  218. type: 'wxPay',
  219. msg: '微信支付API唤起失败',
  220. err,
  221. from: 'uni.requestPayment'
  222. })
  223. }
  224. }
  225. // 校验
  226. function OrderCheckWx() {
  227. orderCheck({
  228. orderId: wxData.orderId
  229. }).then(res => {
  230. if (res.code == 0 && res.data) {
  231. // 校验通过,支付成功
  232. paySuccessResult();
  233. } else {
  234. setTimeout(() => {
  235. orderCheck({
  236. orderId: wxData.orderId
  237. }).then(res2 => {
  238. if (res2.code == 0 && res2.data) {
  239. // 校验通过,支付成功
  240. paySuccessResult();
  241. } else {
  242. uni.hideLoading()
  243. toast('支付二次查验失败,请联系管理员')
  244. checkError && checkError({
  245. type: 'OrderCheckWx',
  246. msg: '支付二次查验失败,请联系管理员',
  247. err: new Error('业务异常,支付二次查验失败'),
  248. form: 'wx'
  249. })
  250. }
  251. }).catch(err1 => {
  252. uni.hideLoading()
  253. if (typeof !isNaN(err1) && err1 === 'number') {
  254. toast('支付二次查验失败,请联系管理员')
  255. }
  256. checkError && checkError({
  257. type: 'OrderCheckWx',
  258. msg: '支付二次查验失败,请联系管理员',
  259. err: err1,
  260. form: 'wx'
  261. })
  262. })
  263. }, 5000)
  264. }
  265. }).catch(err => {
  266. uni.hideLoading()
  267. if (typeof !isNaN(err) && err === 'number') {
  268. toast('支付查验失败')
  269. }
  270. checkError && checkError({
  271. type: 'OrderCheckWx',
  272. msg: '支付查验失败,请联系管理员',
  273. err: err,
  274. form: 'wx'
  275. })
  276. })
  277. }
  278. // 支付宝支付
  279. function aliPay(options) {
  280. Object.assign(aliData, options);
  281. try {
  282. uni.requestPayment({
  283. provider: "alipay",
  284. orderInfo: options.text, //此处为服务器返回的订单信息字符串
  285. success: (res) => {
  286. uni.showLoading({
  287. title: '订单支付中...',
  288. mask: true
  289. });
  290. // 校验支付结果
  291. setTimeout(() => OrderCheckAli(), 1000)
  292. },
  293. fail: (err) => {
  294. if (err.errMsg) {
  295. toast(err.errMsg)
  296. } else {
  297. toast('支付宝支付失败,请联系管理员')
  298. }
  299. payError && payError({
  300. type: 'aliPay',
  301. msg: '支付宝支付失败',
  302. err,
  303. from: 'uni.requestPayment.fail'
  304. })
  305. }
  306. })
  307. } catch (err) {
  308. if (typeof !isNaN(err) && err === 'number') {
  309. toast('支付宝支付环境检测异常')
  310. }
  311. payError && payError({
  312. type: 'aliPay',
  313. msg: '支付宝API唤起失败',
  314. err,
  315. from: "uni.requestPayment"
  316. })
  317. }
  318. }
  319. // 校验
  320. function OrderCheckAli() {
  321. orderCheck({
  322. orderId: aliData.orderId
  323. }).then(res => {
  324. if (res.code == 0 && res.data) {
  325. // 校验通过,支付成功
  326. paySuccessResult();
  327. } else {
  328. setTimeout(() => {
  329. orderCheck({
  330. orderId: aliData.orderId
  331. }).then(res2 => {
  332. if (res2.code == 0 && res2.data) {
  333. // 校验通过,支付成功
  334. paySuccessResult();
  335. } else {
  336. uni.hideLoading()
  337. toast('支付二次查验失败,请联系管理员')
  338. checkError && checkError({
  339. type: 'OrderCheckAli',
  340. msg: '支付二次查验失败,请联系管理员',
  341. err: new Error('业务异常,支付二次查验失败'),
  342. form: 'ali'
  343. })
  344. }
  345. }).catch(err1 => {
  346. uni.hideLoading()
  347. if (typeof !isNaN(err1) && err1 === 'number') {
  348. toast('支付二次查验失败,请联系管理员')
  349. }
  350. checkError && checkError({
  351. type: 'OrderCheckAli',
  352. msg: '支付二次查验失败,请联系管理员',
  353. err: err1,
  354. form: 'ali'
  355. })
  356. })
  357. }, 5000)
  358. }
  359. }).catch(err => {
  360. uni.hideLoading()
  361. if (typeof !isNaN(err) && err === 'number') {
  362. toast('支付查验失败,请联系管理员')
  363. }
  364. checkError && checkError({
  365. type: 'OrderCheckAli',
  366. msg: '支付查验失败,请联系管理员',
  367. err: err,
  368. form: 'ali'
  369. })
  370. })
  371. }
  372. // 苹果支付 --- 无需check 直接购买成功
  373. // 苹果支付 --- 无需check 直接购买成功
  374. function applePay(options) {
  375. Object.assign(appleData, options);
  376. // 核心修复1:提升iapChannel作用域,定义在applePay方法内,所有嵌套回调可访问
  377. let iapChannel = null;
  378. try {
  379. plus.payment.getChannels(
  380. function(channels) {
  381. // uni.hideLoading();
  382. // 获取苹果内购通道,赋值给外层作用域的iapChannel
  383. iapChannel = channels.find((item) => item.id == "appleiap");
  384. if (!iapChannel) {
  385. toast('未找到产品内购信息,请联系管理员');
  386. applePayError && applePayError({
  387. type: "applePay",
  388. msg: "内购商品Id丢失,请确认是否已成功配置",
  389. err: new Error("内购商品Id丢失,请确认是否已成功配置"),
  390. from: "apple",
  391. });
  392. return;
  393. }
  394. const ids = [options.applePid];
  395. iapChannel.requestOrder(
  396. ids,
  397. function(e) {
  398. // 获取订单信息成功回调
  399. console.log('001 获取苹果订单成功', options);
  400. uni.requestPayment({
  401. provider: "appleiap",
  402. orderInfo: {
  403. productid: options.applePid,
  404. quantity: 1,
  405. manualFinishTransaction: true,
  406. },
  407. success: (payRes) => {
  408. // 苹果内购支付成功
  409. console.log('002 苹果支付成功', payRes);
  410. const {
  411. transactionIdentifier: paynum,
  412. transactionReceipt: receipt,
  413. } = payRes;
  414. Object.assign(appleData, {
  415. paynum,
  416. receipt
  417. });
  418. // 核心修复2:then回调内增加try/catch,隔离局部异常
  419. orderPayApple({
  420. chanpinId: appleData.chanpinId,
  421. paynum: paynum,
  422. receipt: receipt,
  423. taocanId: options.taocanId,
  424. }).then((res) => {
  425. try {
  426. uni.hideLoading();
  427. console.log('1231231 orderPayApple接口成功', res);
  428. if (res.code == 0 && res.data) {
  429. // 业务成功,收尾事务(此时iapChannel已存在,作用域正常)
  430. iapChannel.finishTransaction(payRes
  431. .transactionIdentifier);
  432. paySuccessResult(); // 执行支付成功逻辑
  433. } else {
  434. // 业务返回非0码,仍需收尾事务
  435. iapChannel.finishTransaction(payRes
  436. .transactionIdentifier);
  437. toast("业务异常,订单支付失败,请联系管理员");
  438. applePayError && applePayError({
  439. type: "orderPayApple",
  440. msg: "业务异常,订单支付失败",
  441. err: new Error(
  442. `业务返回码异常:${res.code}`),
  443. from: "apple",
  444. });
  445. }
  446. } catch (innerErr) {
  447. // 捕获then回调内的局部异常(如iapChannel意外丢失、res字段缺失等)
  448. console.log('innerErr', innerErr);
  449. uni.hideLoading();
  450. toast("订单处理异常,请联系管理员");
  451. applePayError && applePayError({
  452. type: "orderPayApple_inner",
  453. msg: "订单成功回调内执行异常",
  454. err: innerErr, // 打印具体的局部异常信息
  455. from: "apple",
  456. });
  457. }
  458. }).catch((err) => {
  459. uni.hideLoading();
  460. if (typeof !isNaN(err) && err === 'number') {
  461. // 仅捕获orderPayApple接口请求失败的异常(如网络错误、后端1002码等)
  462. toast("订单支付校验失败catch,请联系管理员");
  463. }
  464. applePayError && applePayError({
  465. type: "orderPayApple",
  466. msg: "订单支付校验失败",
  467. err: err,
  468. from: "apple",
  469. });
  470. });
  471. },
  472. fail: (payErr) => {
  473. uni.hideLoading();
  474. toast("苹果购买失败fail,请联系管理员");
  475. applePayError && applePayError({
  476. type: "uni.requestPayment",
  477. msg: "苹果内购失败",
  478. err: payErr,
  479. from: "apple",
  480. });
  481. },
  482. });
  483. },
  484. function(orderErr) {
  485. // 获取订单信息失败回调
  486. uni.hideLoading();
  487. toast('内购订单获取失败,请联系管理员');
  488. applePayError && applePayError({
  489. type: "iapChannel.requestOrder",
  490. msg: "苹果内购订单获取失败",
  491. err: orderErr,
  492. from: "apple",
  493. });
  494. }
  495. );
  496. },
  497. // 核心修复3:补全plus.payment.getChannels的失败回调(之前缺失)
  498. function(channelErr) {
  499. uni.hideLoading();
  500. toast('获取支付通道失败,请检查内购配置');
  501. applePayError && applePayError({
  502. type: "plus.payment.getChannels",
  503. msg: "获取苹果内购支付通道失败",
  504. err: channelErr,
  505. from: "apple",
  506. });
  507. }
  508. );
  509. } catch (err) {
  510. uni.hideLoading(); // 核心修复4:异常时隐藏loading,避免页面卡死
  511. toast('支付环境检测异常,请联系管理员');
  512. applePayError && applePayError({
  513. type: "applePay_try_catch",
  514. msg: "苹果内购API唤起失败",
  515. err: err,
  516. from: "plus.payment.getChannels",
  517. });
  518. }
  519. }
  520. // 支付成功
  521. function paySuccessResult() {
  522. console.log('3423423423423');
  523. uni.hideLoading()
  524. toast('支付成功')
  525. paySuccess && paySuccess();
  526. }
  527. return {
  528. OrderPay
  529. }
  530. }
  531. /**
  532. * 错误
  533. * 1. 创建订单错误
  534. * 2. 支付SDK错误
  535. * 3. 支付成功后,后台错误
  536. *
  537. *
  538. *
  539. *
  540. *
  541. *
  542. *
  543. *
  544. *
  545. *
  546. *
  547. *
  548. */