PaymentSDKProvider.tsx 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  1. "use client";
  2. import Script from "next/script";
  3. import {
  4. createContext,
  5. useContext,
  6. useState
  7. } from "react";
  8. import { env } from "@/env";
  9. type SDKLoadState = "none" | "loading" | "error" | "success";
  10. type SDKForPaymentMethod = "klarna" | "afterpay" | "clearpay" | "applepay";
  11. type SDKName = "klarna" | "afterpay" | "applepay";
  12. interface PaymentSDKState {
  13. klarna: SDKLoadState;
  14. afterpay: SDKLoadState;
  15. applepay: SDKLoadState;
  16. }
  17. interface PaymentContextProps {
  18. loadSdk: (method:string) => void;
  19. getSDKState: (method:SDKForPaymentMethod) => SDKLoadState | undefined;
  20. loadSDKState: PaymentSDKState;
  21. }
  22. const klarnaSDKURL = "https://x.klarnacdn.net/kp/lib/v1/api.js";
  23. const afterpaySDKURL = env.NEXT_PUBLIC_APP_ENV === "production" ? "https://portal.afterpay.com/afterpay.js" : "https://portal.sandbox.afterpay.com/afterpay.js";
  24. const applepaySDKURL = "https://applepay.cdn-apple.com/jsapi/1.latest/apple-pay-sdk.js";
  25. const PaymentSDKContext = createContext<PaymentContextProps|null>(null);
  26. const normalizeSDKName = (method:string):SDKName| undefined =>{
  27. switch(method){
  28. case "clearpay":
  29. case "afterpay":
  30. return "afterpay";
  31. case "klarna":
  32. return "klarna";
  33. case "applepay":
  34. return "applepay";
  35. default:
  36. return undefined;
  37. }
  38. }
  39. export function PaymentSDKProvider({
  40. children
  41. }: {
  42. children:React.ReactNode;
  43. }){
  44. const [loadSDKState, setLoadSDKState] = useState<PaymentSDKState>({
  45. klarna: 'none', // none loading error success
  46. afterpay: 'none', // none loading error success
  47. applepay: 'none' // none loading error success
  48. });
  49. const loadSdk = (method:string) => {
  50. const m = normalizeSDKName(method);
  51. if(m) {
  52. setLoadSDKState((prev) => {
  53. if(prev[m] !== "none") {
  54. return prev;
  55. }
  56. return {
  57. ...prev,
  58. [m]:"loading"
  59. };
  60. });
  61. }
  62. }
  63. const onSdkLoad = (m:SDKName) => {
  64. setLoadSDKState((prev) => {
  65. return {
  66. ...prev,
  67. [m]: 'success'
  68. }
  69. });
  70. };
  71. const onSdkError = (m:SDKName) => {
  72. setLoadSDKState((prev) => {
  73. return {
  74. ...prev,
  75. [m]: 'error'
  76. }
  77. });
  78. };
  79. const getSDKState = (method:SDKForPaymentMethod) => {
  80. const m = normalizeSDKName(method);
  81. if(m) {
  82. return loadSDKState[m];
  83. } else {
  84. return undefined;
  85. }
  86. }
  87. return (
  88. <PaymentSDKContext.Provider
  89. value={{
  90. loadSdk,
  91. getSDKState,
  92. loadSDKState
  93. }}
  94. >
  95. {loadSDKState.klarna !== "none" &&
  96. <Script id="klarna-sdk" strategy="afterInteractive"
  97. src={klarnaSDKURL}
  98. onLoad={() => onSdkLoad('klarna')}
  99. onError={() => {
  100. onSdkError('klarna');
  101. }}
  102. />
  103. }
  104. { loadSDKState.afterpay !== "none" &&
  105. <Script id="afterpay-sdk" strategy="afterInteractive"
  106. src={afterpaySDKURL}
  107. onLoad={() => onSdkLoad('afterpay')}
  108. onError={() => {
  109. onSdkError('afterpay');
  110. }}
  111. />
  112. }
  113. { loadSDKState.applepay !== "none" &&
  114. <Script id="applepay-sdk" strategy="afterInteractive"
  115. src={applepaySDKURL}
  116. onLoad={() => onSdkLoad('applepay')}
  117. onError={() => {
  118. onSdkError('applepay');
  119. }}
  120. />
  121. }
  122. {children}
  123. </PaymentSDKContext.Provider>
  124. )
  125. }
  126. export const usePaymentSDKContext = () => {
  127. const context = useContext(PaymentSDKContext);
  128. if (!context) {
  129. throw new Error("usePaymentSDKContext must be used within a PaymentSDKProvider");
  130. }
  131. return context;
  132. };