PaymentSDKProvider.tsx 4.1 KB

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