Applepay.php 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275
  1. <?php
  2. namespace Longyi\Pay\Applepay\Payment;
  3. use GuzzleHttp\Client;
  4. use Webkul\Payment\Payment\Payment;
  5. use Webkul\Sales\Models\Order;
  6. use Webkul\Sales\Repositories\OrderRepository;
  7. use Webkul\Sales\Repositories\InvoiceRepository;
  8. class Applepay extends Payment
  9. {
  10. /**
  11. * Payment method code
  12. *
  13. * @var string
  14. */
  15. protected $code = 'applepay';
  16. protected $clientId;
  17. protected $apikey;
  18. protected $webhookId;
  19. protected $currentOrder;
  20. protected $prefix = 'QQS';
  21. public $createOrderApi = 'https://api-m.sandbox.paypal.com/v2/checkout/orders';
  22. public $captureOrderApi = 'https://api-m.sandbox.paypal.com/v2/checkout/orders/{id}/capture';
  23. public $detailOrderApi = 'https://api-m.sandbox.paypal.com/v2/checkout/orders/{id}';
  24. public $updateOrderApi = 'https://api-m.sandbox.paypal.com/v2/checkout/orders/{id}';
  25. public $sigApi = 'https://api-m.sandbox.paypal.com/v1/notifications/verify-webhook-signature';
  26. public $orderTrackApi = 'https://api-m.sandbox.paypal.com/v2/checkout/orders/{id}/track';
  27. public $addTrackApi = 'https://api-m.sandbox.paypal.com/v1/shipping/trackers-batch';
  28. public function __construct(
  29. protected InvoiceRepository $invoiceRepository,
  30. protected OrderRepository $orderRepository,
  31. )
  32. {
  33. if ($this->getConfigData('mode') == 'live') {
  34. $this->createOrderApi = 'https://api-m.paypal.com/v2/checkout/orders';
  35. $this->captureOrderApi = 'https://api-m.paypal.com/v2/checkout/orders/{id}/capture';
  36. $this->detailOrderApi = 'https://api-m.paypal.com/v2/checkout/orders/{id}';
  37. $this->updateOrderApi = 'https://api-m.paypal.com/v2/checkout/orders/{id}';
  38. $this->sigApi = 'https://api-m.paypal.com/v1/notifications/verify-webhook-signature';
  39. $this->orderTrackApi = 'https://api-m.paypal.com/v2/checkout/orders/{id}/track';
  40. $this->addTrackApi = 'https://api-m.paypal.com/v1/shipping/trackers-batch';
  41. }
  42. $this->clientId = $this->getConfigData('client_id');
  43. $this->apikey = $this->getConfigData('secret_id');
  44. $this->webhookId = $this->getConfigData('webhook_id');
  45. }
  46. /**
  47. * Get redirect url.
  48. */
  49. public function getRedirectUrl()
  50. {
  51. }
  52. public function setOrder($order = null): self
  53. {
  54. $this->currentOrder = $order;
  55. return $this;
  56. }
  57. public function getOrder()
  58. {
  59. return $this->currentOrder;
  60. }
  61. public function createGatewayOrder()
  62. {
  63. return $this->createOrder($this->getOrder(), ['merchantOrderId' => $this->getOrder()->increment_id]);
  64. }
  65. public function createOrder($order, $override)
  66. {
  67. $billingAddressLines = $this->getAddressLines($order->billing_address->address);
  68. $data = [
  69. 'intent' => 'AUTHORIZE',
  70. 'purchase_units' => [
  71. [
  72. 'reference_id' => $override['merchantOrderId'],
  73. 'amount' => [
  74. 'currency_code' => $order->order_currency_code,
  75. 'value' => $this->formatCurrencyValue((float) $order->sub_total + $order->tax_total + ($order->selected_shipping_rate ? $order->selected_shipping_rate->price : 0) - $order->discount_amount),
  76. 'breakdown' => [
  77. 'item_total' => [
  78. 'currency_code' => $order->order_currency_code,
  79. 'value' => $this->formatCurrencyValue((float) $order->sub_total),
  80. ],
  81. 'shipping' => [
  82. 'currency_code' => $order->order_currency_code,
  83. 'value' => $this->formatCurrencyValue((float) ($order->selected_shipping_rate ? $order->selected_shipping_rate->price : 0)),
  84. ],
  85. 'tax_total' => [
  86. 'currency_code' => $order->order_currency_code,
  87. 'value' => $this->formatCurrencyValue((float) $order->tax_total),
  88. ],
  89. 'discount' => [
  90. 'currency_code' => $order->order_currency_code,
  91. 'value' => $this->formatCurrencyValue((float) $order->discount_amount),
  92. ],
  93. ],
  94. ],
  95. 'items' => $this->getLineItems($order),
  96. 'shipping' => [
  97. 'address' => [
  98. 'address_line_1' => current($billingAddressLines),
  99. 'address_line_2' => last($billingAddressLines),
  100. 'admin_area_2' => $order->shipping_address->city,
  101. 'admin_area_1' => $order->shipping_address->state,
  102. 'postal_code' => $order->shipping_address->postcode,
  103. 'country_code' => $order->shipping_address->country,
  104. ],
  105. ],
  106. ]
  107. ],
  108. 'payment_source' => [
  109. 'apple_pay' => [
  110. 'name' => $order->customer_full_name,
  111. 'email_address' => $order->customer_email
  112. ]
  113. ]
  114. ];
  115. $client = new Client();
  116. $response = $client->request('POST', $this->createOrderApi, [
  117. 'headers' => [
  118. 'Accept' => 'application/json',
  119. 'Content-Type' => 'application/json',
  120. 'Authorization' => 'Basic '.base64_encode("{$this->clientId}:{$this->apikey}")
  121. ],
  122. 'json' => $data,
  123. 'timeout' => 30,
  124. 'verify' => false
  125. ]);
  126. $resultObject = json_decode($response->getBody()->getContents());
  127. return $resultObject->id;
  128. }
  129. /**
  130. * Return cart items.
  131. *
  132. * @param string $cart
  133. * @return array
  134. */
  135. protected function getLineItems($order)
  136. {
  137. $lineItems = [];
  138. foreach ($order->items as $item) {
  139. $lineItems[] = [
  140. 'unit_amount' => [
  141. 'currency_code' => $order->order_currency_code,
  142. 'value' => $this->formatCurrencyValue((float) $item->price),
  143. ],
  144. 'quantity' => $item->qty_ordered,
  145. 'name' => $item->name,
  146. 'sku' => $item->sku,
  147. 'category' => $item->getTypeInstance()->isStockable() ? 'PHYSICAL_GOODS' : 'DIGITAL_GOODS',
  148. ];
  149. }
  150. return $lineItems;
  151. }
  152. public function webhookSignature($header, $data)
  153. {
  154. $data = [
  155. 'transmission_id' => $header['Paypal-Transmission-Id'],
  156. 'transmission_time' => $header['Paypal-Transmission-Time'],
  157. 'cert_url' => $header['Paypal-Cert-Url'],
  158. 'auth_algo' => $header['Paypal-Auth-Algo'],
  159. 'transmission_sig' => $header['Paypal-Transmission-Sig'],
  160. 'webhook_id' => $this->webhookId,
  161. 'webhook_event' => $data
  162. ];
  163. $client = new Client();
  164. $response = $client->request('POST', $this->sigApi, [
  165. 'headers' => [
  166. 'Accept' => 'application/json',
  167. 'Content-Type' => 'application/json',
  168. 'Authorization' => 'Basic '. base64_encode("{$this->apikey}:{$this->secret}")
  169. ],
  170. 'json' => $data,
  171. 'timeout' => 30,
  172. 'verify' => false
  173. ]);
  174. $resultObject = json_decode($response->getBody()->getContents());
  175. if ($resultObject->verification_status == 'SUCCESS') {
  176. return true;
  177. } else {
  178. return false;
  179. }
  180. }
  181. //成功修改状态
  182. public function paymentSucceeded($orderId, $transactionId)
  183. {
  184. $order = $this->orderRepository->findByField('increment_id', $orderId);
  185. if (!$order->id) {
  186. return false;
  187. }
  188. if ($order->status != Order::STATUS_PENDING) {
  189. return false;
  190. }
  191. $order->status = Order::STATUS_PROCESSING;
  192. $order->save();
  193. if ($order->payment) {
  194. $additional = $order->payment->additional;
  195. $additional['transaction_id'] = $transactionId;
  196. $order->payment->additional = $additional;
  197. $order->payment->save();
  198. }
  199. if ($order->canInvoice()) {
  200. $this->invoiceRepository->create($this->prepareInvoiceData($order));
  201. }
  202. }
  203. /**
  204. * Prepares order's invoice data for creation.
  205. */
  206. protected function prepareInvoiceData($order): array
  207. {
  208. $invoiceData = [
  209. 'order_id' => $order->id,
  210. 'invoice' => ['items' => []],
  211. ];
  212. foreach ($order->items as $item) {
  213. $invoiceData['invoice']['items'][$item->id] = $item->qty_to_invoice;
  214. }
  215. return $invoiceData;
  216. }
  217. protected function getAddressLines($address)
  218. {
  219. $address = explode(PHP_EOL, $address, 2);
  220. $addressLines = [current($address)];
  221. if (isset($address[1])) {
  222. $addressLines[] = str_replace(["\r\n", "\r", "\n"], ' ', last($address));
  223. } else {
  224. $addressLines[] = '';
  225. }
  226. return $addressLines;
  227. }
  228. public function formatCurrencyValue($number): float
  229. {
  230. return round((float) $number, 2);
  231. }
  232. public function isAvailable()
  233. {
  234. if (! parent::isAvailable()) {
  235. return false;
  236. }
  237. $userAgent = request()->header('User-Agent');
  238. if (! $userAgent) {
  239. return false;
  240. }
  241. $appleDevices = [
  242. 'iPhone',
  243. 'iPad',
  244. 'iPod',
  245. 'Macintosh',
  246. 'Mac OS X',
  247. ];
  248. foreach ($appleDevices as $device) {
  249. if (strpos($userAgent, $device) !== false) {
  250. return true;
  251. }
  252. }
  253. return true;
  254. }
  255. }