CheckoutPlaceOrder.tsx 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331
  1. "use client";
  2. import { useRouter } from 'next/navigation'
  3. import {AirwallexCartNumberElementType} from "@/lib/Airwallex/airwallexInit";
  4. import PaypalButton from "./PaymentButton/PaypalButton";
  5. import { overlayLoading } from "@/components/theme/ui/kernel/loading/api";
  6. import { PlaceOrderFunction,KlarnaOtherData } from "@/types/checkout/type";
  7. import { useCustomToast } from "@/utils/hooks/useToast";
  8. import { usePlaceOrder } from "@/utils/hooks/usePlaceOrder";
  9. import { confirmDialog } from "@/components/theme/ui/kernel/confirm/api";
  10. import { useConfig } from '@utils/hooks/useConfig';
  11. import KlarnaButton from "./PaymentButton/KlarnaButton";
  12. export default function CheckoutPlaceOrder({
  13. paymentMethod,
  14. clickPlaceOrder,
  15. validateCheckout,// 校验运输方式,地址
  16. }: {
  17. paymentMethod: string;
  18. clickPlaceOrder: PlaceOrderFunction;
  19. validateCheckout: () => Promise<boolean>;
  20. }) {
  21. const router = useRouter();
  22. const {store} = useConfig();
  23. const { showToast } = useCustomToast();
  24. const { createPaymentCallback } = usePlaceOrder();
  25. const currentCurrencyCode = store.currentCurrency;
  26. const paypalCreateOrder = async () => {
  27. overlayLoading.start();
  28. const valid = await validateCheckout();
  29. if(!valid) {
  30. overlayLoading.stop();
  31. return {
  32. error: true,
  33. msg: '',
  34. orderId: '',
  35. webOrderId: ''
  36. };
  37. }
  38. // 校验通过
  39. const placeOrderRes = await clickPlaceOrder();
  40. if(placeOrderRes.data !== null) {
  41. return {
  42. error: false,
  43. msg: '',
  44. orderId: placeOrderRes.data.gatewayOrderId,
  45. webOrderId: placeOrderRes.data.orderId
  46. };
  47. } else {
  48. return {
  49. error: true,
  50. msg: '',
  51. orderId: '',
  52. webOrderId: ''
  53. };
  54. }
  55. };
  56. const airwallexPlaceOrder = async () => {
  57. overlayLoading.start();
  58. const valid = await validateCheckout();
  59. if(!valid) {
  60. overlayLoading.stop();
  61. return;
  62. }
  63. const placeOrderRes = await clickPlaceOrder({
  64. paymentSuccessUrl: window.location.origin + '/paymentresult/result'
  65. });
  66. overlayLoading.stop();
  67. if(placeOrderRes.data !== null) {
  68. window.location.href = placeOrderRes.data.gatewayOrderId;
  69. }
  70. };
  71. const cardAirwallexPlaceOrder = async () => {
  72. overlayLoading.start();
  73. const valid = await validateCheckout();
  74. if(!valid) {
  75. overlayLoading.stop();
  76. return;
  77. }
  78. const placeOrderRes = await clickPlaceOrder<AirwallexCartNumberElementType>();
  79. if(placeOrderRes.data !== null && placeOrderRes.otherData !== null) {
  80. const webOrderId = placeOrderRes.data.orderId;
  81. const cardNumberElement = placeOrderRes.otherData;
  82. const paymentIntentIdClientSecret = placeOrderRes.data.gatewayOrderId.split(':');
  83. const paymentIntentId = paymentIntentIdClientSecret[0];
  84. const paymentIntentClientSecret = paymentIntentIdClientSecret[1];
  85. cardNumberElement.confirm({
  86. intent_id: paymentIntentId,
  87. client_secret: paymentIntentClientSecret,
  88. }).then((result) => {
  89. overlayLoading.stop();
  90. console.log('cardNumberElement.confirm result ---- ', result);
  91. // 支付成功 跳转页面
  92. router.replace('/paymentresult/result?orderId=' + webOrderId + '&return_result=success');
  93. }).catch((error) => {
  94. overlayLoading.stop();
  95. console.log('cardNumberElement.confirm error ---- ', error);
  96. // 支付失败
  97. router.replace('/paymentresult/result?orderId=' + webOrderId + '&return_result=failure');
  98. });
  99. }
  100. };
  101. const afterpayPlaceOrder = async () => {
  102. if(window.AfterPay) {
  103. overlayLoading.start();
  104. const valid = await validateCheckout();
  105. if(!valid) {
  106. overlayLoading.stop();
  107. return;
  108. }
  109. const placeOrderRes = await clickPlaceOrder({
  110. paymentSuccessUrl: window.location.origin + '/paymentresult/result',
  111. paymentCancelUrl: window.location.origin + '/paymentresult/result'
  112. });
  113. if(placeOrderRes.data !== null) {
  114. const afterpayToken = placeOrderRes.data.gatewayOrderId;
  115. const webOrderId = placeOrderRes.data.orderId;
  116. let countryCode = "US";
  117. if(currentCurrencyCode === "CAD") {
  118. countryCode = "CA";
  119. } else if(currentCurrencyCode === "AUD") {
  120. countryCode = "AU";
  121. } else if(currentCurrencyCode === "NZD") {
  122. countryCode = "NZ";
  123. } else if(currentCurrencyCode === "GBP") {
  124. countryCode = "GB";
  125. }
  126. // if(paymentMethod === "clearpay") {
  127. // countryCode = "GB";
  128. // }
  129. window.AfterPay.initialize({countryCode: countryCode});
  130. // To avoid triggering browser anti-popup rules, the AfterPay.open()
  131. // function must be directly called inside the click event listener
  132. window.AfterPay.open();
  133. // If you don't already have a checkout token at this point, you can
  134. // AJAX to your backend to retrieve one here. The spinning animation
  135. // will continue until `AfterPay.transfer` is called.
  136. // If you fail to get a token you can call AfterPay.close()
  137. window.AfterPay.onComplete = async (event) => {
  138. console.log('window.AfterPay.onComplete event ---- ', event);
  139. const status: "success" | "cancel" | "failure" = event.data.status === "SUCCESS" ? 'success' : 'cancel';
  140. const resCallback = await createPaymentCallback({
  141. orderId: Number(webOrderId),
  142. gatewayOrderId: event.data.orderToken,
  143. status: status,
  144. });
  145. overlayLoading.stop();
  146. if (event.data.status == "SUCCESS") {
  147. // The customer confirmed the payment schedule.
  148. // The token is now ready to be captured from your server backend.
  149. // 跳转结果页 成功
  150. if(!resCallback.error) {
  151. // 后端capture成功,跳转到成功落地页
  152. router.replace('/paymentresult/result?orderId=' + webOrderId + '&return_result=success');
  153. } else {
  154. // capture 失败,提示错误
  155. confirmDialog({
  156. title: "Somthing Wrong",
  157. content: resCallback.msg + " Please contact customer service." + " OrderId: "+ webOrderId,
  158. noCancel: true,
  159. }).then(() => {
  160. router.replace('/');
  161. });
  162. }
  163. } else {
  164. // The customer cancelled the payment or closed the popup window.
  165. // 跳转结果页 取消
  166. router.replace('/paymentresult/result?orderId=' + webOrderId + '&return_result=cancel');
  167. }
  168. }
  169. window.AfterPay.transfer({token: afterpayToken});
  170. }
  171. } else {
  172. showToast("Please wait for the Afterpay to load completely.","warning");
  173. }
  174. };
  175. const klarnaPlaceOrder = async () => {
  176. overlayLoading.start();
  177. const valid = await validateCheckout();
  178. if(!valid) {
  179. overlayLoading.stop();
  180. return;
  181. }
  182. const placeOrderRes = await clickPlaceOrder<KlarnaOtherData>({
  183. paymentSuccessUrl: window.location.origin + '/paymentresult/result',
  184. paymentCancelUrl: window.location.origin + '/paymentresult/result'
  185. });
  186. if(placeOrderRes.data !== null && placeOrderRes.otherData) {
  187. const klarnaToken = placeOrderRes.data.gatewayOrderId;
  188. const webOrderId = placeOrderRes.data.orderId;
  189. const {billingAddress, shippingAddress} = placeOrderRes.otherData;
  190. const klarnaClientToken = klarnaToken.split("|")[0];
  191. const klarnaVerifyToken = klarnaToken.split("|")[1];
  192. window.Klarna.Payments.init({
  193. client_token: klarnaClientToken,
  194. });
  195. //klarna-widget-container
  196. window.Klarna.Payments.load({
  197. container: '#klarna-widget-container',
  198. }, (response) => {
  199. if(!response.show_form) { // klarna不可用
  200. // 跳转结果页
  201. router.replace('/paymentresult/result?orderId=' + webOrderId + '&return_result=failure');
  202. } else {
  203. window.Klarna.Payments.authorize(
  204. {},
  205. {
  206. billing_address: {
  207. given_name: billingAddress.firstName,
  208. family_name: billingAddress.lastName,
  209. email: billingAddress.email,
  210. phone: billingAddress.phone,
  211. street_address: billingAddress.address,
  212. postal_code: billingAddress.postcode,
  213. city: billingAddress.city,
  214. country: billingAddress.country
  215. },
  216. shipping_address: {
  217. given_name: shippingAddress.firstName,
  218. family_name: shippingAddress.lastName,
  219. email: shippingAddress.email,
  220. street_address: shippingAddress.address,
  221. postal_code: shippingAddress.postcode,
  222. city: shippingAddress.city,
  223. phone: shippingAddress.phone,
  224. region: shippingAddress.state,
  225. country: shippingAddress.country
  226. },
  227. // customer: {
  228. // date_of_birth: "1941-03-21",
  229. // },
  230. },
  231. async function(res) {
  232. console.log('klarna authorize res ---- ',res);
  233. if(res.approved && res.authorization_token) { // 可以支付
  234. const resCallback = await createPaymentCallback({
  235. orderId: Number(webOrderId),
  236. gatewayOrderId: res.authorization_token,
  237. verifyToken: klarnaVerifyToken,
  238. status: "success",
  239. });
  240. // 跳转结果页
  241. overlayLoading.stop();
  242. if(!resCallback.error) {
  243. router.replace('/paymentresult/result?orderId=' + webOrderId + '&return_result=success');
  244. } else {
  245. // callback 失败,提示错误
  246. confirmDialog({
  247. title: "Somthing Wrong",
  248. content: resCallback.msg + " Please contact customer service." + " OrderId: "+ webOrderId,
  249. noCancel: true,
  250. }).then(() => {
  251. router.replace('/');
  252. });
  253. }
  254. } else {
  255. // if(res.show_form) { // klarna可用,但是有报错
  256. // } else {
  257. // // klarna 不可用 跳转结果页
  258. // }
  259. overlayLoading.stop();
  260. router.replace('/paymentresult/result?orderId=' + webOrderId + '&return_result=failure');
  261. }
  262. }
  263. );
  264. }
  265. });
  266. }
  267. };
  268. return (
  269. <>
  270. <div className="relative w-full">
  271. {paymentMethod === 'paypal_smart_button' &&
  272. <PaypalButton
  273. createOrder={paypalCreateOrder}
  274. />
  275. }
  276. {(paymentMethod === 'awxklarna' || paymentMethod === 'awxafterpay') &&
  277. <button className="flex items-center justify-center w-full h-12 bg-ly-green text-white rounded-3xl text-ly-16 font-bold"
  278. onClick={airwallexPlaceOrder}
  279. >
  280. Place Order
  281. </button>
  282. }
  283. {paymentMethod === 'airwallex' &&
  284. <button className="flex items-center justify-center w-full h-12 bg-ly-green text-white rounded-3xl text-ly-16 font-bold"
  285. onClick={cardAirwallexPlaceOrder}
  286. >
  287. Place Order
  288. </button>
  289. }
  290. {(paymentMethod === 'afterpay' || paymentMethod === 'clearpay') &&
  291. <button className="flex items-center justify-center w-full h-12 bg-ly-green text-white rounded-3xl text-ly-16 font-bold"
  292. onClick={afterpayPlaceOrder}
  293. >
  294. Place Order
  295. </button>
  296. }
  297. {paymentMethod === 'klarna' &&
  298. <KlarnaButton clickHandler={klarnaPlaceOrder} />
  299. }
  300. </div>
  301. </>
  302. );
  303. }