pay-comp.component.ts 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386
  1. import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
  2. import { CommonModule } from '@angular/common';
  3. import { ActivatedRoute } from '@angular/router';
  4. import { AccountService } from '../../../services/account.service';
  5. import { HttpClient } from '@angular/common/http';
  6. import * as Parse from 'parse';
  7. import {
  8. ionicStandaloneModules,
  9. AlertController,
  10. ToastController,
  11. } from '../../../modules/ionic-standalone.modules';
  12. declare var wx: any;
  13. @Component({
  14. selector: 'app-pay-comp',
  15. templateUrl: './pay-comp.component.html',
  16. styleUrls: ['./pay-comp.component.scss'],
  17. standalone: true,
  18. imports: [...ionicStandaloneModules, CommonModule],
  19. })
  20. export class PayCompComponent implements OnInit {
  21. @Input('price') price!: number;
  22. @Input('credit') credit!: number | undefined; //钻石
  23. @Input('gid') gid: string | undefined;
  24. @Input('orderType') orderType!: string; //订单类型
  25. @Input('tradeNo') tradeNo: string | undefined; //支付单号
  26. @Input('title') title: string = '会员购买';
  27. @Output() payResult: EventEmitter<any> = new EventEmitter<any>();
  28. @Input('orderId') orderId?: string;
  29. isOpen: boolean = true; //打开弹窗
  30. checkpay: string = 'wxpay';
  31. userAgent?: string; //获取当前浏览器环境
  32. codeLink?: string;
  33. user: Parse.Object = Parse.User.current()!;
  34. timer: any; //定时查询
  35. accountLog?: Parse.Object; // 充值记录
  36. constructor(
  37. private accServ: AccountService,
  38. private toastController: ToastController,
  39. private alertCtrl: AlertController,
  40. private http: HttpClient,
  41. private activRoute: ActivatedRoute
  42. ) {}
  43. ngOnInit() {
  44. this.activRoute.paramMap.subscribe((params) => {
  45. let ua = navigator.userAgent.toLowerCase();
  46. if (ua.indexOf('micromessenger') != -1) {
  47. this.userAgent = 'weixin';
  48. this.accServ.getWXSignPackageInWechat();
  49. } else if (ua.indexOf('windows') != -1 || ua.indexOf('Macintosh') != -1) {
  50. this.userAgent = 'pc';
  51. }
  52. });
  53. }
  54. onchangPay(val: string) {
  55. this.checkpay = val;
  56. console.log(val);
  57. }
  58. /* 用户确认支付 */
  59. async userPayment() {
  60. if (!this.tradeNo) {
  61. this.tradeNo = this.accServ.setTradeNo();
  62. } else {
  63. this.orderId = await this.getOrder();
  64. if (this.orderType == 'vip' && !this.orderId) {
  65. this.tradeNo = this.accServ.setTradeNo();
  66. } else if (
  67. this.orderType == 'recharge' &&
  68. (!this.accountLog?.id || this.accountLog?.get('isVerified'))
  69. ) {
  70. this.tradeNo = this.accServ.setTradeNo();
  71. }
  72. }
  73. console.log(this.tradeNo);
  74. switch (this.checkpay) {
  75. case 'wxpay':
  76. this.openWxPay();
  77. break;
  78. case 'alipay':
  79. this.openAlipay();
  80. break;
  81. }
  82. }
  83. /* 校验支付方式 */
  84. async openWxPay() {
  85. let ua = navigator.userAgent.toLowerCase();
  86. let isWeixin = ua.indexOf('micromessenger') != -1;
  87. if (isWeixin) {
  88. //JSAPI 支付
  89. this.wxPayH5();
  90. } else {
  91. this.wxPay();
  92. }
  93. }
  94. isDisabled: boolean = false; //防抖
  95. /* 微信浏览器H5环境 */
  96. async wxPayH5() {
  97. if (this.isDisabled) return;
  98. try {
  99. this.isDisabled = true;
  100. let wechat = Parse.User.current()?.get('wechat');
  101. let openid =
  102. localStorage.getItem('openid') || wechat['wx86a6c35812a41d41']?.openid;
  103. if (openid) {
  104. await this.getOrderId();
  105. let params = {
  106. company: this.accServ.company,
  107. body: this.title,
  108. out_trade_no: this.tradeNo,
  109. total_fee: +this.price,
  110. openid: openid,
  111. appid: 'wx86a6c35812a41d41',
  112. };
  113. let _this = this;
  114. this.http
  115. .post(`https://server.fmode.cn/api/wxpay/neworder`, params)
  116. .subscribe((response) => {
  117. let payinfo: any = response;
  118. wx.chooseWXPay({
  119. timestamp: payinfo.timeStamp, // 支付签名时间戳,注意微信jssdk中的所有使用timestamp字段均为小写。但最新版的支付后台生成签名使用的timeStamp字段名需大写其中的S字符
  120. nonceStr: payinfo.nonceStr, // 支付签名随机串,不长于 32 位
  121. package: payinfo.package, // 统一支付接口返回的prepay_id参数值,提交格式如:prepay_id=\*\*\*)
  122. signType: 'MD5', // 签名方式,默认为'SHA1',使用新版支付需传入'MD5'
  123. paySign: payinfo.paySign, // 支付签名
  124. success: async (res: any) => {
  125. // 支付成功后的回调函数
  126. if (res) {
  127. let info = {
  128. out_trade_no: _this.tradeNo,
  129. nonce_str: payinfo.nonceStr,
  130. company: this.accServ.company,
  131. };
  132. if (this.orderId) {
  133. this.accServ.updateAccountLog(info, this.orderId);
  134. } else if (this.orderType == 'recharge') {
  135. this.accountLog = await this.accServ.updateRecharge(
  136. this.accountLog!
  137. );
  138. }
  139. _this.toast('支付成功', 'success');
  140. _this.isOpen = false;
  141. _this.payResult.emit({
  142. code: 200,
  143. tradeNo: this.tradeNo,
  144. type: this.checkpay,
  145. });
  146. }
  147. },
  148. });
  149. this.isDisabled = false;
  150. });
  151. } else {
  152. // alert('缺少openid,请重新登录')
  153. this.isDisabled = false;
  154. let alert = await this.alertCtrl.create({
  155. header: '提示',
  156. subHeader: '',
  157. message: '缺少openid,请刷新页面或重新登录',
  158. buttons: [
  159. {
  160. role: 'ok',
  161. text: '确认',
  162. handler: () => {
  163. location.reload();
  164. },
  165. },
  166. ],
  167. });
  168. alert.present();
  169. }
  170. } catch (err: any) {
  171. this.isDisabled = false;
  172. let alert = await this.alertCtrl.create({
  173. header: '异常错误',
  174. subHeader: '',
  175. message: err.message,
  176. buttons: [
  177. {
  178. role: 'ok',
  179. text: '确认',
  180. handler: () => {},
  181. },
  182. ],
  183. });
  184. alert.present();
  185. }
  186. }
  187. /* 浏览器二维码支付 */
  188. async wxPay() {
  189. let params = {
  190. company: this.accServ.company,
  191. out_trade_no: this.tradeNo,
  192. total_fee: this.price,
  193. body: this.title,
  194. };
  195. Parse.Cloud.run('pay_code2', params)
  196. .then(async (res) => {
  197. let nonce_str = res.nonce_str;
  198. let codeLink = res.code_url[0];
  199. console.log(codeLink);
  200. await this.getOrderId();
  201. this.updateOrder(nonce_str);
  202. this.isOpen = false;
  203. })
  204. .catch((err) => {
  205. this.isDisabled = false;
  206. this.toast(err.message, 'danger');
  207. });
  208. }
  209. /* 支付宝支付 */
  210. async openAlipay(){
  211. if (this.isDisabled) return;
  212. try {
  213. this.isDisabled = true;
  214. await this.getOrderId();
  215. let data:any = await this.getAliPayUrl();
  216. console.log(data.pay_url);
  217. window.open(data.pay_url, "newW");
  218. this.timer = setInterval(async () => {
  219. console.log("支付结果轮询");
  220. let AccountLog = new Parse.Query("AccountLog");
  221. AccountLog.equalTo("company", this.accServ.company);
  222. AccountLog.equalTo("orderNumber", this.tradeNo);
  223. AccountLog.equalTo("payType", "aliPay");
  224. AccountLog.equalTo("isVerified", true);
  225. let result = await AccountLog.first();
  226. if (result && result.id) {
  227. clearInterval(this.timer);
  228. this.timer = null;
  229. }
  230. if (!this.timer) {
  231. //支付成功返回状态
  232. this.toast('支付成功', 'success');
  233. this.isOpen = false;
  234. this.payResult.emit({
  235. code: 200,
  236. tradeNo: this.tradeNo,
  237. type: this.checkpay,
  238. });
  239. }
  240. }, 1000);
  241. }catch (err: any) {
  242. this.isDisabled = false;
  243. let alert = await this.alertCtrl.create({
  244. header: '异常错误',
  245. subHeader: '',
  246. message: err.message,
  247. buttons: [
  248. {
  249. role: 'ok',
  250. text: '确认',
  251. handler: () => {},
  252. },
  253. ],
  254. });
  255. alert.present();
  256. }
  257. }
  258. // 获取支付宝支付
  259. async getAliPayUrl() {
  260. return new Promise((resolve, reject) => {
  261. let beforURL = window.location.href;
  262. let params = {
  263. company: this.accServ.company,
  264. tradeNo: this.tradeNo,
  265. price: this.price,
  266. tradetype:'wap',
  267. // price: 0.01,
  268. orderTitle: this.title,
  269. returnUrl: beforURL,
  270. };
  271. try {
  272. this.http
  273. .post("https://server.fmode.cn/api/alipay/neworder", params)
  274. .subscribe((res: any) => {
  275. if (res.code !== 200) {
  276. this.toast('网络错误,请稍后重试', 'warning');
  277. return
  278. }
  279. if (res.data && res.data.pay_url) {
  280. resolve(res.data);
  281. }
  282. });
  283. } catch (err) {
  284. if (err) {
  285. this.toast('网络错误,请稍后重试', 'warning');
  286. reject(err);
  287. }
  288. }
  289. });
  290. }
  291. /* 关闭支付弹窗 */
  292. onClose() {
  293. this.isOpen = false;
  294. const result = {
  295. code: 0,
  296. tradeNo: this.tradeNo,
  297. type: this.checkpay,
  298. };
  299. this.payResult.emit(result);
  300. }
  301. async getOrderId() {
  302. if (this.orderType == 'vip' && !this.orderId && this.gid) {
  303. let resulte = await this.accServ.setOrder({
  304. type: 'service', //创建服务类订单
  305. gid: this.gid,
  306. price: this.price,
  307. total_fee: this.price,
  308. tradeNo: this.tradeNo!,
  309. out_trade_no: this.tradeNo!,
  310. payType: this.checkpay,
  311. });
  312. console.log(resulte);
  313. if (resulte?.objectId) {
  314. this.orderId = resulte.objectId;
  315. }
  316. } else if (this.orderType == 'recharge') {
  317. this.accountLog = await this.accServ.creatdRechargeLog({
  318. tradeNo: this.tradeNo!,
  319. payType: this.checkpay,
  320. price: this.price,
  321. credit: this.credit!,
  322. });
  323. }
  324. }
  325. updateOrder(nonce_str: string) {
  326. // 定时查询订单支付状态
  327. let that = this;
  328. that.timer = setInterval(() => {
  329. let info = {
  330. out_trade_no: that.tradeNo,
  331. nonce_str: nonce_str,
  332. company: this.accServ.company,
  333. };
  334. // console.log(info);
  335. Parse.Cloud.run('order_status2', info)
  336. .then(async (res) => {
  337. // console.log(res);
  338. if (res.status && res.status[0] == 'SUCCESS') {
  339. clearInterval(that.timer);
  340. if (this.orderId) {
  341. this.accServ.updateAccountLog(info, this.orderId);
  342. } else if (this.orderType == 'recharge') {
  343. this.accountLog = await this.accServ.updateRecharge(
  344. this.accountLog!
  345. );
  346. }
  347. that.toast('支付成功', 'success');
  348. that.isOpen = false;
  349. that.payResult.emit({
  350. code: 200,
  351. tradeNo: this.tradeNo,
  352. type: this.checkpay,
  353. });
  354. }
  355. })
  356. .catch((err) => {});
  357. }, 3000);
  358. }
  359. async getOrder(): Promise<string | undefined> {
  360. let query = new Parse.Query('Order');
  361. query.notEqualTo('isDeleted', true);
  362. query.equalTo('status', '100');
  363. query.equalTo('orderNum', this.tradeNo);
  364. query.select('objectId');
  365. let res = await query.first();
  366. return res?.id;
  367. }
  368. async toast(text: string, type?: string) {
  369. const toast = await this.toastController.create({
  370. message: text,
  371. color: type ?? 'warning',
  372. duration: 1500,
  373. });
  374. toast.present();
  375. }
  376. }