JoinPay.php 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281
  1. <?php
  2. namespace addons\JoinPay\common\global_func\payment_logic;
  3. use addons\JoinPay\common\logic\util\RequestUtil;
  4. use common\helpers\StringHelper;
  5. use common\models\common\PaymentTrade;
  6. use Yii;
  7. use Exception;
  8. /**
  9. * 汇聚聚合支付类
  10. *
  11. * Class JoinPay
  12. * @package addons\JoinPay\common\logic
  13. */
  14. class JoinPay
  15. {
  16. protected $config;
  17. public $url = 'https://www.joinpay.com/trade/uniPayApi.action';
  18. public $order_query_url = 'https://www.joinpay.com/trade/queryOrder.action';
  19. public $refund_url = 'https://www.joinpay.com/trade/refund.action';
  20. public function __construct($config)
  21. {
  22. $this->config = $config;
  23. }
  24. /**
  25. * 支付接口
  26. * @param array $order
  27. * [
  28. * 'title' => '订单标题'
  29. * 'order_no' => '交易流水号'
  30. * 'amount' => 金额
  31. * 'mall_id' => 商城ID
  32. * 'notify_url' => '回调地址',
  33. * 'type' => '汇聚支付方式'
  34. * 'open_id' => '微信openid'
  35. * ]
  36. * @return array|mixed
  37. * @throws Exception
  38. */
  39. public function pay($order)
  40. {
  41. $request = $this->getRequest($order);
  42. //\Yii::error('request param:'.json_encode($request));
  43. $result = RequestUtil::http_post($this->url, $request);
  44. Yii::error('joinpay result:' . $result);
  45. $result = json_decode($result, true);
  46. if ($result['ra_Code'] == 100 && !empty($result['rc_Result'])) {
  47. if (StringHelper::isJson($result['rc_Result'])) {
  48. $return = json_decode($result['rc_Result'], true);
  49. if (!isset($return['timestamp'])) {
  50. $return['timestamp'] = $return['timeStamp'] ?? '';
  51. }
  52. return $return;
  53. }
  54. return ['result' => $result['rc_Result'], 'rd_Pic' => $result['rd_Pic']];
  55. } else {
  56. //Yii::error('joinpay pay fail,msg:' . json_encode($result));
  57. throw new Exception('汇聚支付有误');
  58. }
  59. }
  60. private function getRequest($order)
  61. {
  62. $request = [
  63. 'p0_Version' => '1.0', //版本号
  64. 'p1_MerchantNo' => $this->config['merchant_no'], //商户编号
  65. 'p2_OrderNo' => $order['order_no'],
  66. 'p3_Amount' => sprintf("%.2f", $order['amount']),
  67. 'p4_Cur' => 1, //人民币
  68. 'p5_ProductName' => $order['title'],
  69. 'p6_ProductDesc' => $order['title'],
  70. 'p7_Mp' => json_encode(['mall_id' => $order['mall_id']]),// 公用回传参数
  71. 'p8_ReturnUrl' => '', //商户页面通知地址
  72. 'p9_NotifyUrl' => $order['notify_url'], // 服务器异步通知地址
  73. 'q1_FrpCode' => $order['type'], //支付宝扫码,扫码方式
  74. 'q2_MerchantBankCode' => '', //银行商户编码
  75. 'q3_SubMerchantNo' => '', //子商户号
  76. 'q4_IsShowPic' => '', //是否展示图片
  77. 'q5_OpenId' => '', //微信 Openid,微信支付必填
  78. 'q6_AuthCode' => '', //付款码数字
  79. 'q7_AppId' => '', //APPID
  80. 'q8_TerminalNo' => '', //终端号
  81. 'q9_TransactionModel' => '', //微信 H5 模式:空,MODEL1,MODEL2
  82. 'qa_TradeMerchantNo' => $this->config['trade_merchant_no'], //报备商户号
  83. ];
  84. if (in_array($order['type'], ['WEIXIN_GZH', 'WEIXIN_XCX', 'WEIXIN_APP3'])) {
  85. //微信环境获取对应的openid
  86. $request['q5_OpenId'] = $order['open_id'] ?? '';
  87. $request['q7_AppId'] = $this->config['wechat_app_id'];
  88. }
  89. $secret = $this->config['merchant_secret'];
  90. $request['hmac'] = urlencode(RequestUtil::hmacRequest($request, $secret));
  91. return $request;
  92. }
  93. /**
  94. * 查询订单支付状态
  95. * @params string $trade_no
  96. * @return array
  97. * @throws
  98. */
  99. public function query($trade_no)
  100. {
  101. $request = [
  102. 'p1_MerchantNo' => $this->config['merchant_no'],
  103. 'p2_OrderNo' => $trade_no,
  104. ];
  105. $secret = $this->config['merchant_secret'];
  106. $request['hmac'] = urlencode(RequestUtil::hmacRequest($request, $secret));
  107. $result = RequestUtil::http_post($this->order_query_url, $request);
  108. return json_decode($result, true);
  109. }
  110. /**
  111. * 退款
  112. * @param array $info
  113. * [
  114. * 'refund_order_no' => $refund_order_no,
  115. * 'out_refund_no' => $out_trade_no,
  116. * 'refund_amount' => 1,
  117. * 'refund_reason' => 1
  118. * ]
  119. *
  120. * @return bool
  121. * @throws
  122. */
  123. public function refund($info)
  124. {
  125. try {
  126. //异步回调地址
  127. $protocol = Yii::$app->params['protocol'];
  128. $url = \Yii::$app->request->hostInfo . \Yii::$app->request->baseUrl . '/pay-notify/' . 'joinpay-refund.php';
  129. if ($protocol) {
  130. $url = str_replace('http://', ($protocol . '://'), $url);
  131. $url = str_replace('https://', ($protocol . '://'), $url);
  132. }
  133. $request = [
  134. 'p1_MerchantNo' => $this->config['merchant_no'], //商户编号
  135. 'p2_OrderNo' => $info['out_refund_no'],
  136. 'p3_RefundOrderNo' => $info['refund_order_no'],
  137. 'p4_RefundAmount' => $info['refund_amount'],
  138. 'p5_RefundReason' => $info['refund_reason'],
  139. 'p6_NotifyUrl' => $url
  140. ];
  141. //商户密钥:
  142. $secret = $this->config['merchant_secret'];
  143. $request['hmac'] = urlencode(RequestUtil::hmacRequest($request, $secret));
  144. $result = RequestUtil::http_post($this->refund_url, $request);
  145. \Yii::error('joinpay refund res:' . $result);
  146. $result = json_decode($result, true);
  147. if ($result['ra_Status'] != 100 || $result['rb_Code'] != 100) {
  148. //失败
  149. throw new Exception($result['rc_CodeMsg']);
  150. }
  151. return true;
  152. } catch (Exception $e) {
  153. Yii::error('joinpay refund fail,msg:' . $e->getMessage());
  154. return false;
  155. }
  156. }
  157. /**
  158. * 验证回调签名
  159. * @param $params
  160. * @return bool
  161. */
  162. private function checkHmac($params)
  163. {
  164. //验证签名
  165. $hmac = $params['hmac'];
  166. unset($params['hmac']);
  167. unset($params['r']);
  168. $hmacVal = urlencode(RequestUtil::hmacRequest($params, $this->config['merchant_secret']));
  169. if ($hmacVal !== $hmac) {
  170. \Yii::error('签名不合法');
  171. return false;
  172. }
  173. return true;
  174. }
  175. /**
  176. * 异步回调
  177. * @return array
  178. * @throws
  179. */
  180. public function notify()
  181. {
  182. $params = array_merge(Yii::$app->request->post(), Yii::$app->request->get());
  183. $params = array_map('urldecode', $params);
  184. //验证签名
  185. if (!$this->checkHmac($params)) {
  186. throw new Exception('签名不合法');
  187. }
  188. if (intval($params['r6_Status']) !== 100) {
  189. //100:支付成功,101:支付失败
  190. throw new Exception('支付状态有误');
  191. }
  192. if ($params['r1_MerchantNo'] !== $this->config['merchant_no']) {
  193. //商户编号不一致
  194. throw new Exception('商户编号不一致');
  195. }
  196. $trade = PaymentTrade::findOne(['out_trade_no' => $params['r2_OrderNo']]);
  197. if (!$trade) {
  198. throw new Exception('订单不存在');
  199. }
  200. $pay_fee = (doubleval($params['r3_Amount']) * 100) . '';
  201. //订单号查询对应订单
  202. $order_amount = (doubleval($trade->getOnlinePayFee()) * 100) . '';
  203. if (intval($pay_fee) !== intval($order_amount)) {
  204. throw new Exception('支付金额与订单金额不一致');
  205. }
  206. //支付回调验证成功
  207. $trade_type = $params['rc_BankCode'] ?? '';
  208. return [
  209. 'out_trade_no' => $trade->out_trade_no,
  210. 'trade_no' => $params['r7_TrxNo'],
  211. 'r9_BankTrxNo' => $params['r9_BankTrxNo']??'',
  212. 'pay_fee' => $params['r3_Amount'],
  213. 'trade_type' => $trade_type,
  214. ];
  215. }
  216. /**
  217. * 退款回调处理
  218. * @param $res
  219. * @return bool
  220. * @throws
  221. */
  222. public function refund_notify($res)
  223. {
  224. //验证签名
  225. if (!$this->checkHmac($res)) {
  226. return false;
  227. }
  228. //r2_OrderNo : 退款流水号
  229. //r3_RefundOrderNo 退款订单号
  230. return true;
  231. }
  232. }