You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

385 lines
10 KiB

7 months ago
  1. import sheep from '@/sheep';
  2. // #ifdef H5
  3. import $wxsdk from '@/sheep/libs/sdk-h5-weixin';
  4. // #endif
  5. import { getRootUrl } from '@/sheep/helper';
  6. import PayOrderApi from '@/sheep/api/pay/order';
  7. /**
  8. * 支付
  9. *
  10. * @param {String} payment = ['wechat','alipay','wallet','mock'] - 支付方式
  11. * @param {String} orderType = ['goods','recharge','groupon'] - 订单类型
  12. * @param {String} id - 订单号
  13. */
  14. export default class SheepPay {
  15. constructor(payment, orderType, id) {
  16. this.payment = payment;
  17. this.id = id;
  18. this.orderType = orderType;
  19. this.payAction();
  20. }
  21. payAction() {
  22. const payAction = {
  23. WechatOfficialAccount: {
  24. wechat: () => {
  25. this.wechatOfficialAccountPay();
  26. },
  27. alipay: () => {
  28. this.redirectPay(); // 现在公众号可以直接跳转支付宝页面
  29. },
  30. wallet: () => {
  31. this.walletPay();
  32. },
  33. mock: () => {
  34. this.mockPay();
  35. },
  36. },
  37. WechatMiniProgram: {
  38. wechat: () => {
  39. this.wechatMiniProgramPay();
  40. },
  41. alipay: () => {
  42. this.copyPayLink();
  43. },
  44. wallet: () => {
  45. this.walletPay();
  46. },
  47. mock: () => {
  48. this.mockPay();
  49. },
  50. },
  51. App: {
  52. wechat: () => {
  53. this.wechatAppPay();
  54. },
  55. alipay: () => {
  56. this.alipay();
  57. },
  58. wallet: () => {
  59. this.walletPay();
  60. },
  61. mock: () => {
  62. this.mockPay();
  63. },
  64. },
  65. H5: {
  66. wechat: () => {
  67. this.wechatWapPay();
  68. },
  69. alipay: () => {
  70. this.redirectPay();
  71. },
  72. wallet: () => {
  73. this.walletPay();
  74. },
  75. mock: () => {
  76. this.mockPay();
  77. },
  78. },
  79. };
  80. return payAction[sheep.$platform.name][this.payment]();
  81. }
  82. // 预支付
  83. prepay(channel) {
  84. return new Promise(async (resolve, reject) => {
  85. let data = {
  86. id: this.id,
  87. channelCode: channel,
  88. channelExtras: {},
  89. };
  90. // 特殊逻辑:微信公众号、小程序支付时,必须传入 openid
  91. if (['wx_pub', 'wx_lite'].includes(channel)) {
  92. const openid = await sheep.$platform.useProvider('wechat').getOpenid();
  93. // 如果获取不到 openid,微信无法发起支付,此时需要引导
  94. if (!openid) {
  95. this.bindWeixin();
  96. return;
  97. }
  98. data.channelExtras.openid = openid;
  99. }
  100. // 发起预支付 API 调用
  101. PayOrderApi.submitOrder(data).then((res) => {
  102. // 成功时
  103. res.code === 0 && resolve(res);
  104. // 失败时
  105. if (res.code !== 0 && res.msg.indexOf('无效的openid') >= 0) {
  106. // 特殊逻辑:微信公众号、小程序支付时,必须传入 openid 不正确的情况
  107. if (
  108. res.msg.indexOf('无效的openid') >= 0 || // 获取的 openid 不正确时,或者随便输入了个 openid
  109. res.msg.indexOf('下单账号与支付账号不一致') >= 0
  110. ) {
  111. // https://developers.weixin.qq.com/community/develop/doc/00008c53c347804beec82aed051c00
  112. this.bindWeixin();
  113. }
  114. }
  115. });
  116. });
  117. }
  118. // #ifdef H5
  119. // 微信公众号 JSSDK 支付
  120. async wechatOfficialAccountPay() {
  121. let { code, data } = await this.prepay('wx_pub');
  122. if (code !== 0) {
  123. return;
  124. }
  125. const payConfig = JSON.parse(data.displayContent);
  126. $wxsdk.wxpay(payConfig, {
  127. success: () => {
  128. this.payResult('success');
  129. },
  130. cancel: () => {
  131. sheep.$helper.toast('支付已手动取消');
  132. },
  133. fail: (error) => {
  134. if (error.errMsg.indexOf('chooseWXPay:没有此SDK或暂不支持此SDK模拟') >= 0) {
  135. sheep.$helper.toast(
  136. '发起微信支付失败,原因:可能是微信开发者工具不支持,建议使用微信打开网页后支付',
  137. );
  138. return;
  139. }
  140. this.payResult('fail');
  141. },
  142. });
  143. }
  144. // 浏览器微信 H5 支付 TODO 芋艿:待接入(注意:H5 支付是给普通浏览器,不是微信公众号的支付,绝大多数人用不到,可以忽略)
  145. async wechatWapPay() {
  146. const { error, data } = await this.prepay();
  147. if (error === 0) {
  148. const redirect_url = `${getRootUrl()}pages/pay/result?id=${this.id}&payment=${
  149. this.payment
  150. }&orderType=${this.orderType}`;
  151. location.href = `${data.pay_data.h5_url}&redirect_url=${encodeURIComponent(redirect_url)}`;
  152. }
  153. }
  154. // 支付链接(支付宝 wap 支付)
  155. async redirectPay() {
  156. let { code, data } = await this.prepay('alipay_wap');
  157. if (code !== 0) {
  158. return;
  159. }
  160. location.href = data.displayContent;
  161. }
  162. // #endif
  163. // 微信小程序支付
  164. async wechatMiniProgramPay() {
  165. // let that = this;
  166. let { code, data } = await this.prepay('wx_lite');
  167. if (code !== 0) {
  168. return;
  169. }
  170. // 调用微信小程序支付
  171. const payConfig = JSON.parse(data.displayContent);
  172. uni.requestPayment({
  173. provider: 'wxpay',
  174. timeStamp: payConfig.timeStamp,
  175. nonceStr: payConfig.nonceStr,
  176. package: payConfig.packageValue,
  177. signType: payConfig.signType,
  178. paySign: payConfig.paySign,
  179. success: (res) => {
  180. this.payResult('success');
  181. },
  182. fail: (err) => {
  183. if (err.errMsg === 'requestPayment:fail cancel') {
  184. sheep.$helper.toast('支付已手动取消');
  185. } else {
  186. this.payResult('fail');
  187. }
  188. },
  189. });
  190. }
  191. // 余额支付
  192. async walletPay() {
  193. const { code } = await this.prepay('wallet');
  194. code === 0 && this.payResult('success');
  195. }
  196. // 模拟支付
  197. async mockPay() {
  198. const { code } = await this.prepay('mock');
  199. code === 0 && this.payResult('success');
  200. }
  201. // 支付宝复制链接支付(通过支付宝 wap 支付实现)
  202. async copyPayLink() {
  203. let { code, data } = await this.prepay('alipay_wap');
  204. if (code !== 0) {
  205. return;
  206. }
  207. // 引入 showModal 点击确认:复制链接;
  208. uni.showModal({
  209. title: '支付宝支付',
  210. content: '复制链接到外部浏览器',
  211. confirmText: '复制链接',
  212. success: (res) => {
  213. if (res.confirm) {
  214. sheep.$helper.copyText(data.displayContent);
  215. }
  216. },
  217. });
  218. }
  219. // 支付宝支付(App)
  220. async alipay() {
  221. let that = this;
  222. const { code, data } = await this.prepay('alipay_app');
  223. if (code !== 0) {
  224. return;
  225. }
  226. uni.requestPayment({
  227. provider: 'alipay',
  228. orderInfo: data.displayContent, // 直接使用返回的支付参数
  229. success: (res) => {
  230. that.payResult('success');
  231. },
  232. fail: (err) => {
  233. if (err.errMsg === 'requestPayment:fail [paymentAlipay:62001]user cancel') {
  234. sheep.$helper.toast('支付已手动取消');
  235. } else {
  236. that.payResult('fail');
  237. }
  238. },
  239. });
  240. }
  241. // 微信支付(App)
  242. async wechatAppPay() {
  243. let that = this;
  244. // 获取预支付信息
  245. let { code, data } = await this.prepay('wx_app');
  246. if (code !== 0) {
  247. sheep.$helper.toast('获取支付信息失败');
  248. return;
  249. }
  250. // 解析支付参数
  251. const payConfig = JSON.parse(data.displayContent);
  252. // 调用微信支付
  253. uni.requestPayment({
  254. provider: 'wxpay',
  255. timeStamp: payConfig.timeStamp,
  256. nonceStr: payConfig.nonceStr,
  257. package: payConfig.packageValue,
  258. signType: payConfig.signType,
  259. paySign: payConfig.paySign,
  260. success: (res) => {
  261. that.payResult('success');
  262. },
  263. fail: (err) => {
  264. if (err.errMsg === 'requestPayment:fail cancel') {
  265. sheep.$helper.toast('支付已手动取消');
  266. } else {
  267. sheep.$helper.toast('支付失败:' + err.errMsg);
  268. that.payResult('fail');
  269. }
  270. }
  271. });
  272. }
  273. // 支付结果跳转,success:成功,fail:失败
  274. payResult(resultType) {
  275. goPayResult(this.id, this.orderType, resultType);
  276. }
  277. // 引导绑定微信
  278. bindWeixin() {
  279. uni.showModal({
  280. title: '微信支付',
  281. content: '请先绑定微信再使用微信支付',
  282. success: function (res) {
  283. if (res.confirm) {
  284. sheep.$platform.useProvider('wechat').bind();
  285. }
  286. },
  287. });
  288. }
  289. }
  290. export function getPayMethods(channels) {
  291. const payMethods = [
  292. {
  293. icon: '/static/img/shop/pay/wechat.png',
  294. title: '微信支付',
  295. value: 'wechat',
  296. disabled: true,
  297. },
  298. {
  299. icon: '/static/img/shop/pay/alipay.png',
  300. title: '支付宝支付',
  301. value: 'alipay',
  302. disabled: true,
  303. },
  304. {
  305. icon: '/static/img/shop/pay/wallet.png',
  306. title: '余额支付',
  307. value: 'wallet',
  308. disabled: true,
  309. },
  310. {
  311. icon: '/static/img/shop/pay/apple.png',
  312. title: 'Apple Pay',
  313. value: 'apple',
  314. disabled: true,
  315. },
  316. {
  317. icon: '/static/img/shop/pay/wallet.png',
  318. title: '模拟支付',
  319. value: 'mock',
  320. disabled: true,
  321. },
  322. ];
  323. const platform = sheep.$platform.name;
  324. // 1. 处理【微信支付】
  325. const wechatMethod = payMethods[0];
  326. if (
  327. (platform === 'WechatOfficialAccount' && channels.includes('wx_pub')) ||
  328. (platform === 'WechatMiniProgram' && channels.includes('wx_lite')) ||
  329. (platform === 'App' && channels.includes('wx_app'))
  330. ) {
  331. wechatMethod.disabled = false;
  332. }
  333. // 2. 处理【支付宝支付】
  334. const alipayMethod = payMethods[1];
  335. if (
  336. (platform === 'H5' && channels.includes('alipay_wap')) ||
  337. (platform === 'WechatOfficialAccount' && channels.includes('alipay_wap')) ||
  338. (platform === 'WechatMiniProgram' && channels.includes('alipay_wap')) ||
  339. (platform === 'App' && channels.includes('alipay_app'))
  340. ) {
  341. alipayMethod.disabled = false;
  342. }
  343. // 3. 处理【余额支付】
  344. const walletMethod = payMethods[2];
  345. if (channels.includes('wallet')) {
  346. walletMethod.disabled = false;
  347. }
  348. // 4. 处理【苹果支付】TODO 芋艿:未来接入
  349. // 5. 处理【模拟支付】
  350. const mockMethod = payMethods[4];
  351. if (channels.includes('mock')) {
  352. mockMethod.disabled = false;
  353. }
  354. return payMethods;
  355. }
  356. // 支付结果跳转,success:成功,fail:失败
  357. export function goPayResult(id, orderType, resultType) {
  358. sheep.$router.redirect('/pages/pay/result', {
  359. id,
  360. orderType,
  361. payState: resultType,
  362. });
  363. }