WithdrawTransferService.php 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. <?php
  2. namespace services\common;
  3. use common\enums\UserWalletEnum;
  4. use common\models\common\PaymentTransfer;
  5. use common\models\mall\MallAddons;
  6. use common\models\user\UserWallet;
  7. use common\models\user\UserWithdrawLog;
  8. use common\models\wechat\WechatTransferLog;
  9. /**
  10. * 提现转账服务
  11. */
  12. class WithdrawTransferService
  13. {
  14. const AUTH_TAG_LENGTH_BYTE = 16;
  15. const AUTH_NOTIFY_STATE = ['FAIL', 'CANCELLED']; // 只有转账失败或者订单已撤销时才需要回退金额
  16. /**
  17. * 微信转账回调
  18. */
  19. public function notify(array $data, string $appKey)
  20. {
  21. try {
  22. $resource = $data['resource'] ?? [];
  23. $resourceDecrypt = json_decode($this->transferDecrypt($resource, $appKey), true) ?? [];
  24. if (empty($resourceDecrypt)) {
  25. return json_encode(['code' => 'FAIL', 'message' => '操作失败']);
  26. }
  27. if (in_array($resourceDecrypt['state'], self::AUTH_NOTIFY_STATE)) {
  28. // 获取提现流水号
  29. $paymentTransfer = PaymentTransfer::find()->where(['order_no' => $resourceDecrypt['out_bill_no']])->asArray()->one();
  30. if (!$paymentTransfer) {
  31. return json_encode(['code' => 'FAIL', 'message' => '支付记录不存在']);
  32. }
  33. $withdrawal_source = $paymentTransfer['withdrawal_source'] ?? '';
  34. switch ($withdrawal_source) {
  35. case 'AiyunuoStoreWithdrawLog':
  36. if (MallAddons::isInstall($paymentTransfer->mall_id, 'Aiyunuo')) {
  37. $withdraw = new \addons\Aiyunuo\common\service\WithdrawTransferService();
  38. $res = $withdraw->refund($paymentTransfer);
  39. } else {
  40. $res = true;
  41. }
  42. break;
  43. case 'MchWithdrawLog':
  44. if (MallAddons::isInstall($paymentTransfer->mall_id, 'Mch')) {
  45. $withdraw = new \addons\Mch\common\services\WithdrawTransferService();
  46. $res = $withdraw->refund($paymentTransfer);
  47. } else {
  48. $res = true;
  49. }
  50. break;
  51. case 'LaoDaFu':
  52. if (MallAddons::isInstall($paymentTransfer->mall_id, 'LaoDaFu')) {
  53. $withdraw = new \addons\LaoDaFu\common\services\WithdrawTransferService();
  54. $res = $withdraw->refund($paymentTransfer);
  55. } else {
  56. $res = true;
  57. }
  58. break;
  59. case 'Store':
  60. if (MallAddons::isInstall($paymentTransfer->mall_id, 'Store')) {
  61. $withdraw = new \addons\Store\common\services\WithdrawTransferService();
  62. $res = $withdraw->refund($paymentTransfer);
  63. } else {
  64. $res = true;
  65. }
  66. break;
  67. case 'HigoWithdrawLog':
  68. $res = true;
  69. break;
  70. default:
  71. $res = $this->refund($paymentTransfer);
  72. break;
  73. }
  74. return $res;
  75. }
  76. $out_bill_no = $resourceDecrypt['out_bill_no'];
  77. $wechatTransferLog = \common\models\wechat\WechatTransferLog::find()->where(['out_trade_no' => $out_bill_no])->one();
  78. if(!empty($wechatTransferLog)){
  79. $transferResult = json_decode($wechatTransferLog->transfer_result,true);
  80. $transferResult['state'] = $resourceDecrypt['state'];
  81. $wechatTransferLog->transfer_result = json_encode($transferResult);
  82. $wechatTransferLog->save();
  83. }
  84. return true;
  85. } catch (\Exception $e) {
  86. \Yii::$app->custom->logs("提现转账回调失败", $e->getMessage());
  87. return false;
  88. }
  89. }
  90. /**
  91. * 微信转账数据解密
  92. * @param $resource
  93. * @param $appKey
  94. * @return false|string
  95. */
  96. protected function transferDecrypt($resource, $appKey)
  97. {
  98. $ciphertext = base64_decode($resource['ciphertext']);
  99. $nonce = $resource['nonce'];
  100. $text = substr($ciphertext, 0, -self::AUTH_TAG_LENGTH_BYTE);
  101. $authTag = substr($ciphertext, -self::AUTH_TAG_LENGTH_BYTE);
  102. $associatedData = $resource['associated_data'];
  103. return openssl_decrypt($text, 'aes-256-gcm', $appKey, \OPENSSL_RAW_DATA, $nonce,
  104. $authTag, $associatedData);
  105. }
  106. /**
  107. * 回退提现
  108. */
  109. protected function refund(array $data): bool
  110. {
  111. $insertWallet = [];
  112. try {
  113. // 根据流水号获取提现记录
  114. $withdrawLog = UserWithdrawLog::find()->where(['id' => $data['withdrawal_source_id']])->one();
  115. if (!$withdrawLog) {
  116. throw new \Exception("提现记录不存在");
  117. }
  118. // 修改提现状态
  119. $withdrawLog->status = 4;
  120. if (!$withdrawLog->save()) {
  121. throw new \Exception("提现状态修改失败");
  122. }
  123. $insertWallet[] = [
  124. 'mall_id' => $withdrawLog->mall_id,
  125. 'user_id' => $withdrawLog->user_id,
  126. 'wallet_type' => $withdrawLog->from == 1 ? 'commission' : 'balance',
  127. 'is_frozen' => false,
  128. 'not_add_total' => 1,
  129. 'money' => $withdrawLog->num,
  130. 'desc' => '提现未领取回退',
  131. 'source_table' => 'user_wallet',
  132. 'from_type' => UserWalletEnum::WITHDRAWAL,
  133. 'capital_type' => UserWalletEnum::WITHDRAWAL_REFUND,
  134. 'source_table_id' => $withdrawLog->id,
  135. ];
  136. if (!empty($insertWallet)) {
  137. // 因为退款了并未扣减已提现金额。所以需要扣减已提现金额
  138. $user = UserWallet::findOne(['mall_id' => $withdrawLog->mall_id, 'user_id' => $withdrawLog->user_id]);
  139. if ($user) {
  140. if ($withdrawLog->from == 1) {
  141. $user->commission_withdrawal -= $withdrawLog->num;
  142. } else {
  143. $user->balance_withdrawal -= $withdrawLog->num;
  144. }
  145. $user->save();
  146. }
  147. $res = UserWalletService::batchHandleByQueue($insertWallet);
  148. if (empty($res)) throw new \Exception(UserWalletService::getStaticError());
  149. }
  150. return true;
  151. } catch (\Exception $e) {
  152. return false;
  153. }
  154. }
  155. }