BitPay.php 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
  1. <?php
  2. /**
  3. * BitPay - 更匿名、更安全、无需支付宝姓名和账户等个人私密信息
  4. *
  5. * Date: 2019/5/17
  6. * Time: 1:08 AM PST
  7. */
  8. namespace App\Services\Gateway;
  9. use App\Models\Paylist;
  10. use App\Services\Auth;
  11. use App\Services\Config;
  12. use App\Services\View;
  13. class BitPay extends AbstractPayment
  14. {
  15. private $bitpayAppSecret;
  16. private $bitpayGatewayUri;
  17. /*****************************************************************
  18. * BitPay Helper Function
  19. ******************************************************************/
  20. public function __construct($bitpayAppSecret)
  21. {
  22. $this->bitpayAppSecret = $bitpayAppSecret;
  23. $this->bitpayGatewayUri = 'https://api.mugglepay.com/v1/';
  24. }
  25. public function prepareSignId($tradeno)
  26. {
  27. $data_sign = array();
  28. $data_sign['merchant_order_id'] = $tradeno;
  29. $data_sign['secret'] = $this->bitpayAppSecret;
  30. ksort($data_sign);
  31. return http_build_query($data_sign);
  32. }
  33. public function sign($data)
  34. {
  35. return strtolower(md5(md5($data) . $this->bitpayAppSecret));
  36. }
  37. public function verify($data, $signature)
  38. {
  39. $mySign = $this->sign($data);
  40. return $mySign === $signature;
  41. }
  42. public function mprequest($data, $type = 'pay')
  43. {
  44. $headers = array('content-type: application/json', 'token: ' . $this->bitpayAppSecret);
  45. $curl = curl_init();
  46. if ($type === 'pay') {
  47. $this->bitpayGatewayUri .= 'orders';
  48. curl_setopt($curl, CURLOPT_URL, $this->bitpayGatewayUri);
  49. curl_setopt($curl, CURLOPT_POST, 1);
  50. $data_string = json_encode($data);
  51. curl_setopt($curl, CURLOPT_POSTFIELDS, $data_string);
  52. } elseif ($type === 'query') {
  53. $this->bitpayGatewayUri .= 'orders/merchant_order_id/status?id=';
  54. $this->bitpayGatewayUri .= $data['merchant_order_id'];
  55. curl_setopt($curl, CURLOPT_URL, $this->bitpayGatewayUri);
  56. curl_setopt($curl, CURLOPT_HTTPGET, 1);
  57. }
  58. curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
  59. curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
  60. curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
  61. curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false);
  62. $data = curl_exec($curl);
  63. curl_close($curl);
  64. return $data;
  65. }
  66. public function query($tradeNo)
  67. {
  68. $data['merchant_order_id'] = $tradeNo;
  69. return json_decode($this->mprequest($data, 'query'), true);
  70. }
  71. /*****************************************************************
  72. * Abstract Payment Implentation
  73. ******************************************************************/
  74. public function purchase($request, $response, $args)
  75. {
  76. $price = $request->getParam('price');
  77. $type = $request->getParam('type');
  78. // file_put_contents(BASE_PATH.'/bitpay_purchase.log', $price . " " . $type . "\r\n", FILE_APPEND);
  79. if ($price <= 0) {
  80. return json_encode(['errcode' => -1, 'errmsg' => '非法的金额.']);
  81. }
  82. $user = Auth::getUser();
  83. $pl = new Paylist();
  84. $pl->userid = $user->id;
  85. $pl->total = $price;
  86. $pl->tradeno = self::generateGuid();
  87. $pl->save();
  88. $data['merchant_order_id'] = $pl->tradeno;
  89. $data['price_amount'] = (float)$price;
  90. $data['price_currency'] = 'CNY';
  91. $data['title'] = '支付单号' . $pl->tradeno;
  92. $data['description'] = '充值:' . $price;
  93. $data['description'] .= ' 元';
  94. $data['callback_url'] = Config::get('baseUrl') . '/payment/bitpay/notify';
  95. $data['success_url'] = Config::get('baseUrl') . '/user/payment/bitpay/return?merchantTradeNo=';
  96. $data['success_url'] .= $pl->tradeno;
  97. $data['cancel_url'] = $data['success_url'];
  98. if ($type === 'Alipay') {
  99. $data['checkout'] = 'QBIT';
  100. }
  101. $str_to_sign = $this->prepareSignId($pl->tradeno);
  102. $data['token'] = $this->sign($str_to_sign);
  103. $result = json_decode($this->mprequest($data), true);
  104. $result['pid'] = $pl->tradeno;
  105. // file_put_contents(BASE_PATH.'/bitpay_purchase.log', json_encode($data)."\r\n", FILE_APPEND);
  106. // file_put_contents(BASE_PATH.'/bitpay_purchase.log', json_encode($result)."\r\n", FILE_APPEND);
  107. if ($result['status'] == 200 || $result['status'] == 201) {
  108. $result['payment_url'] .= '&lang=zh';
  109. return json_encode(array('url' => $result['payment_url'], 'errcode' => 0, 'pid' => $pl->id));
  110. }
  111. return json_encode(['errcode' => -1, 'errmsg' => $result . error]);
  112. }
  113. public function notify($request, $response, $args)
  114. {
  115. if (!$this->bitpayAppSecret || $this->bitpayAppSecret === '') {
  116. $return = [];
  117. $return['status'] = 400;
  118. echo json_encode($return);
  119. return;
  120. }
  121. $inputString = file_get_contents('php://input', 'r');
  122. $inputStripped = str_replace(array("\r", "\n", "\t", "\v"), '', $inputString);
  123. $inputJSON = json_decode($inputStripped, true); //convert JSON into array
  124. $data = array();
  125. if ($inputJSON !== null) {
  126. $data['status'] = $inputJSON['status'];
  127. $data['order_id'] = $inputJSON['order_id'];
  128. $data['merchant_order_id'] = $inputJSON['merchant_order_id'];
  129. $data['price_amount'] = $inputJSON['price_amount'];
  130. $data['price_currency'] = $inputJSON['price_currency'];
  131. $data['created_at_t'] = $inputJSON['created_at_t'];
  132. }
  133. // file_put_contents(BASE_PATH.'/bitpay_notify.log', json_encode($data)."\r\n", FILE_APPEND);
  134. // 准备待签名数据
  135. $str_to_sign = $this->prepareSignId($inputJSON['merchant_order_id']);
  136. $resultVerify = $this->verify($str_to_sign, $inputJSON['token']);
  137. $isPaid = $data !== null && $data['status'] !== null && $data['status'] === 'PAID';
  138. // file_put_contents(BASE_PATH.'/bitpay_notify.log', $resultVerify."\r\n".$isPaid."\r\n", FILE_APPEND);
  139. if ($resultVerify && $isPaid) {
  140. $this->postPayment($data['merchant_order_id'], 'BitPay');
  141. // echo 'SUCCESS';
  142. $return = [];
  143. $return['status'] = 200;
  144. echo json_encode($return);
  145. } else {
  146. // echo 'FAIL';
  147. $return = [];
  148. $return['status'] = 400;
  149. echo json_encode($return);
  150. }
  151. }
  152. public function getPurchaseHTML()
  153. {
  154. return View::getSmarty()->fetch('user/bitpay.tpl');
  155. }
  156. public function getReturnHTML($request, $response, $args)
  157. {
  158. $pid = $_GET['merchantTradeNo'];
  159. $p = Paylist::where('tradeno', '=', $pid)->first();
  160. $money = $p->total;
  161. if ($p->status == 1) {
  162. $success = 1;
  163. } else {
  164. $data = $this->query($pid);
  165. $isPaid = $data !== null && $data['order'] !== null && $data['order']['status'] !== null && $data['order']['status'] === 'PAID';
  166. // file_put_contents(BASE_PATH.'/bitpay_return.log', $pid . " " . $data ."\r\n". $isPaid . "\r\n", FILE_APPEND);
  167. if ($isPaid) {
  168. $this->postPayment($pid, 'BitPay');
  169. $success = 1;
  170. } else {
  171. $success = 0;
  172. }
  173. }
  174. return View::getSmarty()->assign('money', $money)->assign('success', $success)->fetch('user/pay_success.tpl');
  175. }
  176. public function getStatus($request, $response, $args)
  177. {
  178. $return = [];
  179. $p = Paylist::where('tradeno', $_POST['pid'])->first();
  180. $return['ret'] = 1;
  181. $return['result'] = $p->status;
  182. // file_put_contents(BASE_PATH.'/bitpay_status_success.log', json_encode($return)."\r\n", FILE_APPEND);
  183. return json_encode($return);
  184. }
  185. }