index.tsx 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368
  1. import {
  2. Button,
  3. Cell,
  4. CellGroup,
  5. Icon,
  6. RadioGroup,
  7. Radio,
  8. Dialog,
  9. Toast
  10. } from 'vant'
  11. import { computed, defineComponent, reactive } from 'vue'
  12. import styles from './index.module.less'
  13. import { browser, moneyFormat } from '@/helpers/utils'
  14. import { useEventTracking } from '@/helpers/hooks'
  15. import request from '@/helpers/request'
  16. import { useRouter } from 'vue-router'
  17. import { state as baseState } from '@/state'
  18. import activeButtonIcon from '@common/images/icon_checkbox.png'
  19. import inactiveButtonIcon from '@common/images/icon_checkbox_default.png'
  20. import activeButtonIconTenant from '@common/images/icon_checkbox-tenant.png'
  21. import iconWechat from '@common/images/icon-wechat.png'
  22. import iconAlipay from '@common/images/icon-alipay.png'
  23. import {
  24. listenerMessage,
  25. postMessage,
  26. removeListenerMessage
  27. } from '@/helpers/native-message'
  28. const urlFix =
  29. baseState.platformType === 'TEACHER' ? '/api-teacher' : '/api-student'
  30. export default defineComponent({
  31. name: 'payment',
  32. props: {
  33. modelValue: {
  34. type: Boolean,
  35. default: false
  36. },
  37. paymentConfig: {
  38. type: Object,
  39. default: () => ({})
  40. },
  41. paymentVendor: {
  42. type: String,
  43. default: ''
  44. },
  45. paymentChannels: {
  46. type: Array,
  47. default: () => []
  48. }
  49. },
  50. emits: ['backOut', 'close', 'confirm', 'update:modelValue'],
  51. setup(props, { emit }) {
  52. const router = useRouter()
  53. const state = reactive({
  54. payType: 'wx',
  55. pay_channel: ''
  56. })
  57. const onClose = () => {
  58. // 继续支付则直接关闭弹窗就可
  59. Dialog.confirm({
  60. message: '是否放弃本次付款',
  61. confirmButtonText: '继续付款',
  62. cancelButtonText: '放弃'
  63. })
  64. .then(() => {
  65. //
  66. })
  67. .catch(async () => {
  68. // this.onCancel()
  69. useEventTracking('取消支付')
  70. await onCancel()
  71. emit('close')
  72. useEventTracking('取消支付')
  73. })
  74. }
  75. const isWxPay = computed(() => {
  76. if (
  77. props.paymentVendor === 'yeepay' ||
  78. props.paymentVendor === 'adapay'
  79. ) {
  80. return true
  81. }
  82. const channelStr = props.paymentChannels?.join(',')
  83. if (channelStr && channelStr.indexOf('wxpay') !== -1) {
  84. return true
  85. }
  86. return false
  87. })
  88. const isAliPay = computed(() => {
  89. if (
  90. props.paymentVendor === 'yeepay' ||
  91. props.paymentVendor === 'adapay'
  92. ) {
  93. return true
  94. }
  95. const channelStr = props.paymentChannels?.join(',')
  96. if (channelStr && channelStr.indexOf('alipay') !== -1) {
  97. return true
  98. }
  99. return false
  100. })
  101. // 需要关闭订单
  102. // orderPay: {
  103. // cancelUrl: urlFix + '/userOrder/orderCancel',
  104. // payUrl: urlFix + '/userOrder/orderPay'
  105. // }
  106. const onCancel = async (noBack?: boolean) => {
  107. try {
  108. await request.post(urlFix + '/userOrder/orderCancel', {
  109. data: {
  110. orderNo: props.paymentConfig.orderNo
  111. }
  112. })
  113. } catch {
  114. //
  115. }
  116. // 不管接口是否报错,都返回
  117. emit('update:modelValue', false)
  118. !noBack && router.go(-1)
  119. emit('backOut')
  120. }
  121. const onSubmit = async () => {
  122. if (
  123. props.paymentVendor &&
  124. (props.paymentVendor.indexOf('wxpay') > -1 ||
  125. props.paymentVendor.indexOf('alipay') > -1)
  126. ) {
  127. submitNativePay()
  128. } else {
  129. submitQrCodePay()
  130. }
  131. }
  132. // 支付方式,使用原生支付
  133. const submitNativePay = async () => {
  134. // 支付...
  135. try {
  136. const payChannel = state.payType === 'zfb' ? 'ali_app' : 'wx_app'
  137. console.log(props.paymentConfig, 'paymentConfig')
  138. let paymentType = props.paymentVendor
  139. props.paymentChannels.forEach((item: any) => {
  140. if (state.payType === 'zfb' && item.indexOf('alipay') !== -1) {
  141. paymentType = item
  142. }
  143. if (state.payType === 'wx' && item.indexOf('wxpay') !== -1) {
  144. paymentType = item
  145. }
  146. })
  147. const params = {
  148. // orderNo: props.paymentConfig.orderNo,
  149. merOrderNo: props.paymentConfig.orderNo,
  150. paymentChannel: payChannel,
  151. paymentVendor: paymentType
  152. }
  153. const res = await request.post(
  154. urlFix + '/userOrder/executePayment/v2',
  155. {
  156. data: {
  157. ...params
  158. }
  159. }
  160. )
  161. postMessage({
  162. api: 'paymentOrder',
  163. content: {
  164. orderNo: props.paymentConfig.orderNo,
  165. payChannel,
  166. // payInfo: `alipays://platformapi/startapp?saId=10000007&qrcode=${res.data.pay_info}`
  167. payInfo: res.data.pay_info
  168. }
  169. })
  170. Toast.loading({
  171. message: '支付中...',
  172. forbidClick: true,
  173. duration: 3000,
  174. loadingType: 'spinner'
  175. })
  176. Toast.clear()
  177. emit('update:modelValue', false)
  178. // 唤起支付时状态
  179. listenerMessage('paymentOperation', result => {
  180. console.log(result, 'init paymentOperation')
  181. paymentOperation(result?.content)
  182. })
  183. } catch (e: any) {
  184. console.log(e)
  185. }
  186. }
  187. const paymentOperation = (res: any) => {
  188. console.log(res, 'paymentOperation')
  189. // 支付状态
  190. // paymentOperation 支付成功:success 支付失败:error 支付取消:cancel 未安装:fail
  191. // error 只有安卓端有
  192. if (res.status === 'success' || res.status === 'error') {
  193. Toast.clear()
  194. emit('update:modelValue', false)
  195. router.replace({
  196. path: '/tradeDetail',
  197. query: {
  198. orderNo: props.paymentConfig.orderNo
  199. }
  200. })
  201. } else if (res.status === 'cancel') {
  202. Toast.clear()
  203. emit('update:modelValue', false)
  204. } else if (res.status === 'fail') {
  205. const message =
  206. state.payType === 'zfb' ? '您尚未安装支付宝' : '您尚未安装微信'
  207. Dialog.alert({
  208. title: '提示',
  209. message
  210. }).then(() => {
  211. Toast.clear()
  212. emit('update:modelValue', false)
  213. })
  214. }
  215. }
  216. // 支付方式,汇付,易宝支付
  217. const submitQrCodePay = () => {
  218. // 支付...
  219. const pt = state.payType
  220. // 判断当前浏览器
  221. if (browser().weixin) {
  222. // 微信浏览器
  223. if (pt == 'zfb') {
  224. state.pay_channel = 'alipay_qr'
  225. getCodePay('qrCode')
  226. } else if (pt == 'wx') {
  227. state.pay_channel = 'wx_pub'
  228. getCodePay('pay')
  229. }
  230. } else if (browser().alipay) {
  231. // 支付宝浏览器
  232. if (pt == 'zfb') {
  233. state.pay_channel = 'alipay_wap'
  234. // 支付宝 H5 支付
  235. getCodePay('pay')
  236. } else if (pt == 'wx') {
  237. state.pay_channel = 'wx_pub'
  238. getCodePay('qrCode')
  239. }
  240. } else {
  241. if (pt == 'zfb') {
  242. state.pay_channel = 'alipay_qr'
  243. } else if (pt == 'wx') {
  244. state.pay_channel = 'wx_pub'
  245. }
  246. getCodePay('qrCode')
  247. }
  248. }
  249. const getCodePay = (code: any) => {
  250. // 二维码页面, 唤起支付页面
  251. const payCode = code == 'qrCode' ? 'payCenter' : 'payResult'
  252. emit('confirm', {
  253. payCode,
  254. pay_channel: state.pay_channel
  255. })
  256. }
  257. return () => (
  258. <div class={styles.payment}>
  259. <Icon onClick={onClose} name="cross" size={20} />
  260. <div class={[styles.title]}>选择支付方式</div>
  261. <div class={styles.payAmount}>
  262. <p>应付金额</p>
  263. <div class={styles.amount}>
  264. <span>¥</span>
  265. {moneyFormat(props.paymentConfig.actualPrice)}
  266. </div>
  267. </div>
  268. <RadioGroup v-model={state.payType}>
  269. <CellGroup border={false}>
  270. {isWxPay.value && (
  271. <Cell
  272. border={true}
  273. center
  274. onClick={() => {
  275. state.payType = 'wx'
  276. }}
  277. v-slots={{
  278. icon: () => <Icon name={iconWechat} size={18} />,
  279. 'right-icon': () => (
  280. <Radio
  281. name="wx"
  282. v-slots={{
  283. icon: (props: any) => (
  284. <Icon
  285. class={styles.boxStyle}
  286. name={
  287. props.checked
  288. ? baseState.projectType === 'tenant'
  289. ? activeButtonIconTenant
  290. : activeButtonIcon
  291. : inactiveButtonIcon
  292. }
  293. />
  294. )
  295. }}
  296. />
  297. ),
  298. title: () => (
  299. <div class={styles.payTypeRe}>
  300. 微信支付 <span class={styles.recommend}>推荐</span>
  301. </div>
  302. )
  303. }}
  304. ></Cell>
  305. )}
  306. {isAliPay.value && (
  307. <Cell
  308. title="支付宝支付"
  309. border={true}
  310. center
  311. onClick={() => {
  312. state.payType = 'zfb'
  313. }}
  314. v-slots={{
  315. icon: () => <Icon name={iconAlipay} size={18} />,
  316. 'right-icon': () => (
  317. <Radio
  318. name="zfb"
  319. v-slots={{
  320. icon: (props: any) => (
  321. <Icon
  322. class={styles.boxStyle}
  323. name={
  324. props.checked
  325. ? baseState.projectType === 'tenant'
  326. ? activeButtonIconTenant
  327. : activeButtonIcon
  328. : inactiveButtonIcon
  329. }
  330. />
  331. )
  332. }}
  333. />
  334. )
  335. }}
  336. ></Cell>
  337. )}
  338. </CellGroup>
  339. </RadioGroup>
  340. <div class={styles.blank}></div>
  341. <Button
  342. type="primary"
  343. class={[
  344. styles.payBtn,
  345. baseState.projectType === 'tenant' && styles.tenantPayBtn
  346. ]}
  347. block
  348. round
  349. onClick={onSubmit}
  350. >
  351. 确认支付
  352. </Button>
  353. </div>
  354. )
  355. }
  356. })