Stripe.php 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  1. <?php
  2. namespace App\Http\Controllers\Gateway;
  3. use App\Models\Payment;
  4. use Auth;
  5. use Exception;
  6. use Illuminate\Http\JsonResponse;
  7. use Log;
  8. use Response;
  9. use Stripe\Checkout\Session;
  10. use Stripe\Exception\SignatureVerificationException;
  11. use Stripe\Source;
  12. use Stripe\Webhook;
  13. use UnexpectedValueException;
  14. class Stripe extends AbstractPayment
  15. {
  16. public function __construct()
  17. {
  18. \Stripe\Stripe::setApiKey(sysConfig('stripe_secret_key'));
  19. }
  20. public function purchase($request): JsonResponse
  21. {
  22. $type = $request->input('type');
  23. $payment = $this->creatNewPayment(Auth::id(), $request->input('id'), $request->input('amount'));
  24. if ($type == 1 || $type == 3) {
  25. $stripe_currency = sysConfig('stripe_currency');
  26. $ch = curl_init();
  27. $url = 'https://api.exchangerate-api.com/v4/latest/'.strtoupper($stripe_currency);
  28. curl_setopt($ch, CURLOPT_URL, $url);
  29. curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
  30. curl_setopt($ch, CURLOPT_HEADER, 0);
  31. $currency = json_decode(curl_exec($ch));
  32. curl_close($ch);
  33. $price_exchanged = bcdiv((float) $payment->amount, $currency->rates->CNY, 10);
  34. $source = Source::create([
  35. 'amount' => floor($price_exchanged * 100),
  36. 'currency' => $stripe_currency,
  37. 'type' => $type == 1 ? 'alipay' : 'wechat',
  38. 'statement_descriptor' => $payment->trade_no,
  39. 'metadata' => [
  40. 'user_id' => $payment->user_id,
  41. 'out_trade_no' => $payment->trade_no,
  42. 'identifier' => '',
  43. ],
  44. 'redirect' => [
  45. 'return_url' => route('invoice'),
  46. ],
  47. ]);
  48. if ($type == 3) {
  49. if (! $source['wechat']['qr_code_url']) {
  50. Log::warning('创建订单错误:未知错误');
  51. $payment->delete();
  52. return response()->json(['code' => 0, 'msg' => '创建订单失败:未知错误']);
  53. }
  54. $payment->update(['qr_code' => 1, 'url' => $source['wechat']['qr_code_url']]);
  55. return Response::json(['status' => 'success', 'data' => $payment->trade_no, 'message' => '创建订单成功!']);
  56. } else {
  57. if (! $source['redirect']['url']) {
  58. Log::warning('创建订单错误:未知错误');
  59. $payment->delete();
  60. return response()->json(['code' => 0, 'msg' => '创建订单失败:未知错误']);
  61. }
  62. $payment->update(['url' => $source['redirect']['url']]);
  63. return Response::json(['status' => 'success', 'url' => $source['redirect']['url'], 'message' => '创建订单成功!']);
  64. }
  65. } else {
  66. $data = $this->getCheckoutSessionData($payment->trade_no, $payment->amount, $type);
  67. try {
  68. $session = Session::create($data);
  69. $url = route('stripe.checkout', ['session_id' => $session->id]);
  70. $payment->update(['url' => $url]);
  71. return Response::json(['status' => 'success', 'url' => $url, 'message' => '创建订单成功!']);
  72. } catch (Exception $e) {
  73. Log::error('【Stripe】错误: '.$e->getMessage());
  74. exit;
  75. }
  76. }
  77. }
  78. protected function getCheckoutSessionData(string $tradeNo, int $amount, int $type): array
  79. {
  80. $unitAmount = $amount * 100;
  81. return [
  82. 'payment_method_types' => ['card'],
  83. 'line_items' => [
  84. [
  85. 'price_data' => [
  86. 'currency' => 'usd',
  87. 'product_data' => ['name' => sysConfig('subject_name') ?: sysConfig('website_name')],
  88. 'unit_amount' => $unitAmount,
  89. ],
  90. 'quantity' => 1,
  91. ],
  92. ],
  93. 'mode' => 'payment',
  94. 'success_url' => route('invoice'),
  95. 'cancel_url' => route('invoice'),
  96. 'client_reference_id' => $tradeNo,
  97. 'customer_email' => Auth::getUser()->email,
  98. ];
  99. }
  100. // redirect to Stripe Payment url
  101. public function redirectPage($session_id)
  102. {
  103. return view('user.components.payment.stripe', ['session_id' => $session_id]);
  104. }
  105. // url = '/callback/notify?method=stripe'
  106. public function notify($request): void
  107. {
  108. $sigHeader = $_SERVER['HTTP_STRIPE_SIGNATURE'];
  109. $endpointSecret = sysConfig('stripe_signing_secret');
  110. $payload = @file_get_contents('php://input');
  111. try {
  112. $event = Webhook::constructEvent($payload, $sigHeader, $endpointSecret);
  113. } catch (UnexpectedValueException $e) {
  114. // Invalid payload
  115. http_response_code(400);
  116. exit();
  117. } catch (SignatureVerificationException $e) {
  118. // Invalid signature
  119. http_response_code(400);
  120. exit();
  121. }
  122. Log::info('【Stripe】Passed signature verification!');
  123. switch ($event->type) {
  124. case 'checkout.session.completed':
  125. /* @var $session Session */
  126. $session = $event->data->object;
  127. // Check if the order is paid (e.g., from a card payment)
  128. //
  129. // A delayed notification payment will have an `unpaid` status, as
  130. // you're still waiting for funds to be transferred from the customer's
  131. // account.
  132. if ($session->payment_status == 'paid') {
  133. // Fulfill the purchase
  134. $this->paymentReceived($session->client_reference_id);
  135. }
  136. break;
  137. case 'checkout.session.async_payment_succeeded':
  138. $session = $event->data->object;
  139. // Fulfill the purchase
  140. $this->paymentReceived($session->client_reference_id);
  141. break;
  142. case 'checkout.session.async_payment_failed':
  143. $session = $event->data->object;
  144. // Send an email to the customer asking them to retry their order
  145. $this->failedPayment($session);
  146. break;
  147. }
  148. http_response_code(200);
  149. exit();
  150. }
  151. // 未支付成功则关闭订单
  152. public function failedPayment(Session $session)
  153. {
  154. $payment = Payment::whereTradeNo($session->client_reference_id)->first();
  155. if ($payment) {
  156. $payment->order->close();
  157. }
  158. }
  159. }