AlternativePay.php 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299
  1. <?php
  2. namespace common\components\payment;
  3. use addons\Mch\common\models\MchWithdrawLog;
  4. use addons\Store\common\models\StoreWithdrawLog;
  5. use common\components\payment\joinpay\util\RequestUtil;
  6. use common\enums\AddonsEnum;
  7. use common\helpers\Url;
  8. use common\models\common\PaidSinglePayLog;
  9. use common\models\mall\MallAddons;
  10. use common\models\user\UserWithdrawLog;
  11. use Yii;
  12. use Exception;
  13. use yii\helpers\Json;
  14. /**
  15. * 汇聚支付:代付
  16. * Class AlternativePay
  17. * @package app\lib\payment\AlternativePay
  18. */
  19. class AlternativePay
  20. {
  21. public $config = [];
  22. //protected $pay_code = 'joinpay_scan_pay';
  23. //单笔代付
  24. public $single_pay_url = 'https://www.joinpay.com/payment/pay/singlePay';
  25. //单笔代付查询接口
  26. public $single_query_url = 'https://www.joinpay.com/payment/pay/singlePayQuery';
  27. //代付接口状态
  28. const STATUS_CREATE = 201; // 201,订单已创建,汇聚受理并创建代付订单的初始状态
  29. const STATUS_WAIT = 202; //202,待商户审核,代付订单处在等待商户审核的阶段
  30. const STATUS_CHECKING = 203; //203,交易处理中,代付订单处理中状态
  31. const STATUS_FAIL = 204; //204,交易失败,明确交易失败的状态码
  32. const STATUS_SUCCESS = 205; //205,交易成功,明确交易成功的状态码
  33. const STATUS_CANCEL = 208; //208,订单已取消,代付订单被商户审核为拒绝付款
  34. const STATUS_FROZEN = 210; //210,账务冻结中,确认出款后对商户的账户余额进行冻结处理
  35. const STATUS_UNFROZEN = 211; //211,账务解冻中,确认失败后对商户的账户余额进行解冻处理
  36. const STATUS_CANCELING = 212; // 212,订单取消中,商户审核为拒绝付款的中间状态
  37. const STATUS_DEDUCTING = 213; //213,账务扣款中,确认成功后对商户的账户冻结款的扣款处理
  38. const STATUS_NOTEXIST = 214; //214,订单不存在,汇聚未受理该笔代付请求,找不到该笔订单,明确失败
  39. public function __construct($cfg = [], $mall_id)
  40. {
  41. if (empty($cfg)) {
  42. $cfg = $this->getConfig($mall_id);
  43. }
  44. $this->config = $cfg;
  45. }
  46. /**
  47. * 汇聚支付:代付
  48. * @param $paymentTransfer
  49. * @param $userInfo
  50. * @return bool
  51. * @throws Exception
  52. * @throws \yii\db\Exception
  53. */
  54. protected function format($bank_account){
  55. $s = $bank_account;
  56. $count = strlen($s);
  57. $bank_account = '';
  58. $regex ="/^[0-9]+$/i";
  59. for ($i=0;$i<$count;$i++){
  60. if (preg_match($regex, $s[$i])) {
  61. $bank_account.=$s[$i];
  62. }
  63. }
  64. return $bank_account;
  65. }
  66. public function transfer($paymentTransfer, $userInfo): bool
  67. {
  68. $cash_info = $paymentTransfer->getCashInfo();
  69. $cash_extra = Json::decode($cash_info->extra, true);
  70. //使用提现表中的订单号:
  71. $order_no = $cash_info->order_no;
  72. $single_pay_log = PaidSinglePayLog::findOne(['order_no' => $order_no, 'status' => [PaidSinglePayLog::STATUS_SUCCESS, PaidSinglePayLog::STATUS_CHECKING]]);
  73. if ($single_pay_log) {
  74. //记录已存在
  75. throw new Exception('记录已存在');
  76. }
  77. $t = \Yii::$app->db->beginTransaction();
  78. try {
  79. $model = new PaidSinglePayLog();
  80. $model->order_no = $order_no;
  81. $model->mall_id = $paymentTransfer->mall_id;
  82. $model->type = 'BANK_PAY_DAILY_ORDER';
  83. $model->receive_account = $this->format($cash_extra['bank_account']);
  84. $model->receive_name = $cash_extra['name'];
  85. $model->account_type = $cash_extra['bank_account_type'] == 1 ? 201 : 204;
  86. $model->receive_bank_chanel_no = (string) $cash_extra['receive_bank_chanel_no'] ?? '';
  87. $model->amount = $paymentTransfer->amount;
  88. $model->currency = 201;
  89. $model->is_checked = 202;
  90. $model->paid_desc = $paymentTransfer->title;
  91. $model->paid_use = 201;
  92. $model->source = $paymentTransfer->source;
  93. $model->created_at = time();
  94. $model->merchant_order_no = PaidSinglePayLog::createMerchantOrderNo();
  95. if (!$model->save()) {
  96. throw new Exception(isset($model->errors) ? current($model->errors)[0] : '数据异常!');
  97. }
  98. $res = $this->singlePay($model->merchant_order_no, $paymentTransfer->amount);
  99. if ($res) {
  100. //修改
  101. $paymentTransfer->is_pay = 1;
  102. if (!$paymentTransfer->save()) {
  103. throw new Exception(isset($paymentTransfer->errors) ? current($paymentTransfer->errors)[0] : '数据异常!');
  104. }
  105. }
  106. $t->commit();
  107. return true;
  108. } catch (Exception $e) {
  109. $t->rollBack();
  110. throw $e;
  111. }
  112. }
  113. /**
  114. * 单笔代付
  115. * @param string $order_no
  116. * @param float $amount
  117. * @return bool
  118. * @throws
  119. */
  120. public function singlePay(string $order_no, float $amount): bool
  121. {
  122. $single_pay_log = PaidSinglePayLog::findOne(['merchant_order_no' => $order_no]);
  123. if (!$single_pay_log) {
  124. return false;
  125. }
  126. if ($single_pay_log->amount != $amount) {
  127. return false;
  128. }
  129. // self::getConfig($single_pay_log->mall_id);
  130. // $productCode = [
  131. // 'BANK_PAY_ORDINARY_ORDER'=>'普通代付',
  132. // 'BANK_PAY_DAILY_ORDER'=>'朝夕付',
  133. // 'BANK_PAY_MAT_ENDOWMENT_ORDER'=>'任意付',
  134. // 'BANK_PAY_COMPOSE_ORDER'=>'组合付',
  135. // ];
  136. // $protocol = env('PAY_NOTIFY_PROTOCOL');
  137. //$callbackUrl = \Yii::$app->request->hostInfo . \Yii::$app->request->baseUrl . '?r=api/pay-notify/joinpay-single';
  138. $api_host = \Yii::$app->services->setting->platform->get('system.api_url');
  139. //https://api.qijianshigw.com/notify/joinpay-single
  140. $callbackUrl = $api_host.'/notify/joinpay-single';
  141. // $callbackUrl = Url::toApi(['notify/joinpay-single'], true);
  142. \Yii::error('JoinpaySinglePay notify url:'.$callbackUrl);
  143. $request = [
  144. 'userNo' => $this->config['joinpay_merchant_no'], //商户编号
  145. 'productCode' => $single_pay_log->type, //产品类型,选择:BANK_PAY_DAILY_ORDER 朝夕付
  146. 'requestTime' => date("Y-m-d H:i:s", time()), //交易请求时间
  147. 'merchantOrderNo' => $order_no, //商户订单号,必填
  148. 'receiverAccountNoEnc' => $single_pay_log->receive_account, //收款账户号,必填
  149. 'receiverNameEnc' => $single_pay_log->receive_name, //收款人,必填
  150. 'receiverAccountType' => $single_pay_log->account_type, //账户类型,必填
  151. 'receiverBankChannelNo' => $single_pay_log->receive_bank_chanel_no ?? '', //收款账户联行号
  152. 'paidAmount' => sprintf("%.2f", $amount), //交易金额
  153. 'currency' => $single_pay_log->currency, //币种:人民币201,美金202
  154. 'isChecked' => $single_pay_log->is_checked, //是否复核
  155. 'paidDesc' => $single_pay_log->paid_desc, //代付说明
  156. 'paidUse' => $single_pay_log->paid_use, //代付用途
  157. 'callbackUrl' => $callbackUrl, //商户通知地址
  158. //'firstProductCode' => '', //优先使用产品,组合付必填,非组合付不用填写):
  159. ];
  160. //商户密钥:
  161. $secret = $this->config['joinpay_merchant_secret'];
  162. $request['hmac'] = urlencode(RequestUtil::hmacRequest($request, $secret));
  163. \Yii::error('JoinpaySinglePay post_data:'.json_encode($request));
  164. $result = RequestUtil::http_post($this->single_pay_url, $request, true);
  165. //保存接口返回信息
  166. $single_pay_log->result = $result;
  167. $single_pay_log->save();
  168. \Yii::error('alternativePay result:' . $result);
  169. $result = Json::decode($result, true);
  170. if ($result['statusCode'] == 2001) {
  171. return true;
  172. }
  173. throw new Exception($result['message']);
  174. }
  175. /**
  176. * 查询代付订单状态
  177. * @param $order_no
  178. * @return bool|mixed|string
  179. */
  180. public function queryAlternativeOrder($order_no)
  181. {
  182. $request = [
  183. 'userNo' => $this->config['joinpay_merchant_no'], //商户编号
  184. 'merchantOrderNo' => $order_no, //商户订单号,必填
  185. ];
  186. //商户密钥:
  187. $secret = $this->config['joinpay_merchant_secret'];
  188. $request['hmac'] = urlencode(RequestUtil::hmacRequest($request, $secret));
  189. $result = RequestUtil::http_post($this->single_query_url, $request, true);
  190. \Yii::error('queryAlternativeOrder result:' . $result);
  191. return Json::decode($result, true);
  192. }
  193. /**
  194. * 获取支付配置
  195. * @author zmz
  196. * @time 2021-05-08
  197. */
  198. private function getConfig($mall_id)
  199. {
  200. //$cfg = \Yii::$app->services->setting->mall->get($mall_id, 'pay', 'joinpay');
  201. $cfg = \Yii::$app->services->setting->addons->get($mall_id, AddonsEnum::ADDONS_NAME_JOIN_PAY, 'basic');
  202. $this->config = $cfg;
  203. return $this->config;
  204. //$this->config = Json::decode($cfg['joinpay']);
  205. //return Json::decode($cfg['joinpay']);
  206. }
  207. /**
  208. * 修改代付状态
  209. * @param PaidSinglePayLog $single_pay_log
  210. * @param $query_info
  211. * @return bool
  212. * @throws Exception
  213. */
  214. public function updateSinglePayStatus($single_pay_log,$query_info)
  215. {
  216. if($query_info['statusCode'] !== 2001){
  217. //受理失败
  218. return ;
  219. }
  220. $single_pay_log->result = json_encode($query_info);
  221. $status = PaidSinglePayLog::STATUS_CHECKING;
  222. if(in_array($query_info['data']['status'],[self::STATUS_SUCCESS])){
  223. //成功
  224. $status = PaidSinglePayLog::STATUS_SUCCESS;
  225. $single_pay_log->error_msg = $query_info['data']['errorDesc'] ?? '交易成功';
  226. }elseif (in_array($query_info['data']['status'],[self::STATUS_FAIL,self::STATUS_CANCEL,self::STATUS_NOTEXIST])){
  227. //失败
  228. $status = PaidSinglePayLog::STATUS_ERROR;
  229. $single_pay_log->error_msg = $query_info['data']['errorDesc'] ?? '';
  230. }else{
  231. //其他状态:处理中
  232. }
  233. if($status == $single_pay_log->status){
  234. return ;
  235. }
  236. $single_pay_log->status = $status;
  237. if(!$single_pay_log->save()){
  238. throw new Exception($single_pay_log->getErrorMessage());
  239. }
  240. switch ($single_pay_log->source){
  241. case '2':
  242. if(MallAddons::isInstall($single_pay_log->mall_id,'Store')){
  243. if($status == PaidSinglePayLog::STATUS_ERROR) {
  244. StoreWithdrawLog::remit_fail(StoreWithdrawLog::findOne(['order_no' => $single_pay_log->order_no]), $single_pay_log->error_msg);
  245. }
  246. }
  247. break;
  248. case '3':
  249. if(MallAddons::isInstall($single_pay_log->mall_id,'Mch')){
  250. if($status == PaidSinglePayLog::STATUS_ERROR) {
  251. MchWithdrawLog::remit_fail(StoreWithdrawLog::findOne(['order_no' => $single_pay_log->order_no]), $single_pay_log->error_msg);
  252. }
  253. }
  254. break;
  255. default:
  256. if($status == PaidSinglePayLog::STATUS_ERROR) {
  257. UserWithdrawLog::remit_fail(UserWithdrawLog::findOne(['order_no' => $single_pay_log->order_no]), $single_pay_log->error_msg);
  258. }
  259. if($status == PaidSinglePayLog::STATUS_SUCCESS) {
  260. UserWithdrawLog::remit_success(UserWithdrawLog::findOne(['order_no' =>$single_pay_log->order_no ]));
  261. }
  262. }
  263. return true;
  264. }
  265. }