@easypayment/medusa-paypal 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (272) hide show
  1. package/.medusa/server/src/admin/index.js +2127 -0
  2. package/.medusa/server/src/admin/index.mjs +2128 -0
  3. package/.medusa/server/src/api/admin/payment-collections/[id]/payment-sessions/route.d.ts +3 -0
  4. package/.medusa/server/src/api/admin/payment-collections/[id]/payment-sessions/route.d.ts.map +1 -0
  5. package/.medusa/server/src/api/admin/payment-collections/[id]/payment-sessions/route.js +25 -0
  6. package/.medusa/server/src/api/admin/payment-collections/[id]/payment-sessions/route.js.map +1 -0
  7. package/.medusa/server/src/api/admin/paypal/audit-logs/route.d.ts +3 -0
  8. package/.medusa/server/src/api/admin/paypal/audit-logs/route.d.ts.map +1 -0
  9. package/.medusa/server/src/api/admin/paypal/audit-logs/route.js +12 -0
  10. package/.medusa/server/src/api/admin/paypal/audit-logs/route.js.map +1 -0
  11. package/.medusa/server/src/api/admin/paypal/disconnect/route.d.ts +3 -0
  12. package/.medusa/server/src/api/admin/paypal/disconnect/route.d.ts.map +1 -0
  13. package/.medusa/server/src/api/admin/paypal/disconnect/route.js +9 -0
  14. package/.medusa/server/src/api/admin/paypal/disconnect/route.js.map +1 -0
  15. package/.medusa/server/src/api/admin/paypal/disputes/[id]/route.d.ts +3 -0
  16. package/.medusa/server/src/api/admin/paypal/disputes/[id]/route.d.ts.map +1 -0
  17. package/.medusa/server/src/api/admin/paypal/disputes/[id]/route.js +17 -0
  18. package/.medusa/server/src/api/admin/paypal/disputes/[id]/route.js.map +1 -0
  19. package/.medusa/server/src/api/admin/paypal/disputes/route.d.ts +3 -0
  20. package/.medusa/server/src/api/admin/paypal/disputes/route.d.ts.map +1 -0
  21. package/.medusa/server/src/api/admin/paypal/disputes/route.js +27 -0
  22. package/.medusa/server/src/api/admin/paypal/disputes/route.js.map +1 -0
  23. package/.medusa/server/src/api/admin/paypal/disputes/summary/route.d.ts +3 -0
  24. package/.medusa/server/src/api/admin/paypal/disputes/summary/route.d.ts.map +1 -0
  25. package/.medusa/server/src/api/admin/paypal/disputes/summary/route.js +17 -0
  26. package/.medusa/server/src/api/admin/paypal/disputes/summary/route.js.map +1 -0
  27. package/.medusa/server/src/api/admin/paypal/environment/route.d.ts +4 -0
  28. package/.medusa/server/src/api/admin/paypal/environment/route.d.ts.map +1 -0
  29. package/.medusa/server/src/api/admin/paypal/environment/route.js +23 -0
  30. package/.medusa/server/src/api/admin/paypal/environment/route.js.map +1 -0
  31. package/.medusa/server/src/api/admin/paypal/onboard-complete/route.d.ts +8 -0
  32. package/.medusa/server/src/api/admin/paypal/onboard-complete/route.d.ts.map +1 -0
  33. package/.medusa/server/src/api/admin/paypal/onboard-complete/route.js +41 -0
  34. package/.medusa/server/src/api/admin/paypal/onboard-complete/route.js.map +1 -0
  35. package/.medusa/server/src/api/admin/paypal/onboarding-link/route.d.ts +4 -0
  36. package/.medusa/server/src/api/admin/paypal/onboarding-link/route.d.ts.map +1 -0
  37. package/.medusa/server/src/api/admin/paypal/onboarding-link/route.js +35 -0
  38. package/.medusa/server/src/api/admin/paypal/onboarding-link/route.js.map +1 -0
  39. package/.medusa/server/src/api/admin/paypal/onboarding-status/route.d.ts +3 -0
  40. package/.medusa/server/src/api/admin/paypal/onboarding-status/route.d.ts.map +1 -0
  41. package/.medusa/server/src/api/admin/paypal/onboarding-status/route.js +20 -0
  42. package/.medusa/server/src/api/admin/paypal/onboarding-status/route.js.map +1 -0
  43. package/.medusa/server/src/api/admin/paypal/reconciliation-status/route.d.ts +3 -0
  44. package/.medusa/server/src/api/admin/paypal/reconciliation-status/route.d.ts.map +1 -0
  45. package/.medusa/server/src/api/admin/paypal/reconciliation-status/route.js +8 -0
  46. package/.medusa/server/src/api/admin/paypal/reconciliation-status/route.js.map +1 -0
  47. package/.medusa/server/src/api/admin/paypal/rotate-credentials/route.d.ts +3 -0
  48. package/.medusa/server/src/api/admin/paypal/rotate-credentials/route.d.ts.map +1 -0
  49. package/.medusa/server/src/api/admin/paypal/rotate-credentials/route.js +9 -0
  50. package/.medusa/server/src/api/admin/paypal/rotate-credentials/route.js.map +1 -0
  51. package/.medusa/server/src/api/admin/paypal/save-credentials/route.d.ts +3 -0
  52. package/.medusa/server/src/api/admin/paypal/save-credentials/route.d.ts.map +1 -0
  53. package/.medusa/server/src/api/admin/paypal/save-credentials/route.js +13 -0
  54. package/.medusa/server/src/api/admin/paypal/save-credentials/route.js.map +1 -0
  55. package/.medusa/server/src/api/admin/paypal/settings/route.d.ts +4 -0
  56. package/.medusa/server/src/api/admin/paypal/settings/route.d.ts.map +1 -0
  57. package/.medusa/server/src/api/admin/paypal/settings/route.js +14 -0
  58. package/.medusa/server/src/api/admin/paypal/settings/route.js.map +1 -0
  59. package/.medusa/server/src/api/admin/paypal/status/route.d.ts +3 -0
  60. package/.medusa/server/src/api/admin/paypal/status/route.d.ts.map +1 -0
  61. package/.medusa/server/src/api/admin/paypal/status/route.js +11 -0
  62. package/.medusa/server/src/api/admin/paypal/status/route.js.map +1 -0
  63. package/.medusa/server/src/api/store/payment-collections/[id]/payment-sessions/route.d.ts +3 -0
  64. package/.medusa/server/src/api/store/payment-collections/[id]/payment-sessions/route.d.ts.map +1 -0
  65. package/.medusa/server/src/api/store/payment-collections/[id]/payment-sessions/route.js +43 -0
  66. package/.medusa/server/src/api/store/payment-collections/[id]/payment-sessions/route.js.map +1 -0
  67. package/.medusa/server/src/api/store/paypal/capture-order/route.d.ts +3 -0
  68. package/.medusa/server/src/api/store/paypal/capture-order/route.d.ts.map +1 -0
  69. package/.medusa/server/src/api/store/paypal/capture-order/route.js +215 -0
  70. package/.medusa/server/src/api/store/paypal/capture-order/route.js.map +1 -0
  71. package/.medusa/server/src/api/store/paypal/config/route.d.ts +3 -0
  72. package/.medusa/server/src/api/store/paypal/config/route.d.ts.map +1 -0
  73. package/.medusa/server/src/api/store/paypal/config/route.js +45 -0
  74. package/.medusa/server/src/api/store/paypal/config/route.js.map +1 -0
  75. package/.medusa/server/src/api/store/paypal/create-order/route.d.ts +3 -0
  76. package/.medusa/server/src/api/store/paypal/create-order/route.d.ts.map +1 -0
  77. package/.medusa/server/src/api/store/paypal/create-order/route.js +305 -0
  78. package/.medusa/server/src/api/store/paypal/create-order/route.js.map +1 -0
  79. package/.medusa/server/src/api/store/paypal/disputes/route.d.ts +3 -0
  80. package/.medusa/server/src/api/store/paypal/disputes/route.d.ts.map +1 -0
  81. package/.medusa/server/src/api/store/paypal/disputes/route.js +46 -0
  82. package/.medusa/server/src/api/store/paypal/disputes/route.js.map +1 -0
  83. package/.medusa/server/src/api/store/paypal/settings/route.d.ts +3 -0
  84. package/.medusa/server/src/api/store/paypal/settings/route.d.ts.map +1 -0
  85. package/.medusa/server/src/api/store/paypal/settings/route.js +14 -0
  86. package/.medusa/server/src/api/store/paypal/settings/route.js.map +1 -0
  87. package/.medusa/server/src/api/store/paypal/webhook/route.d.ts +3 -0
  88. package/.medusa/server/src/api/store/paypal/webhook/route.d.ts.map +1 -0
  89. package/.medusa/server/src/api/store/paypal/webhook/route.js +203 -0
  90. package/.medusa/server/src/api/store/paypal/webhook/route.js.map +1 -0
  91. package/.medusa/server/src/jobs/paypal-reconcile.d.ts +7 -0
  92. package/.medusa/server/src/jobs/paypal-reconcile.d.ts.map +1 -0
  93. package/.medusa/server/src/jobs/paypal-reconcile.js +131 -0
  94. package/.medusa/server/src/jobs/paypal-reconcile.js.map +1 -0
  95. package/.medusa/server/src/jobs/paypal-webhook-retry.d.ts +7 -0
  96. package/.medusa/server/src/jobs/paypal-webhook-retry.d.ts.map +1 -0
  97. package/.medusa/server/src/jobs/paypal-webhook-retry.js +78 -0
  98. package/.medusa/server/src/jobs/paypal-webhook-retry.js.map +1 -0
  99. package/.medusa/server/src/modules/paypal/clients/paypal-seller.client.d.ts +14 -0
  100. package/.medusa/server/src/modules/paypal/clients/paypal-seller.client.d.ts.map +1 -0
  101. package/.medusa/server/src/modules/paypal/clients/paypal-seller.client.js +65 -0
  102. package/.medusa/server/src/modules/paypal/clients/paypal-seller.client.js.map +1 -0
  103. package/.medusa/server/src/modules/paypal/index.d.ts +92 -0
  104. package/.medusa/server/src/modules/paypal/index.d.ts.map +1 -0
  105. package/.medusa/server/src/modules/paypal/index.js +13 -0
  106. package/.medusa/server/src/modules/paypal/index.js.map +1 -0
  107. package/.medusa/server/src/modules/paypal/migrations/20260115120000_create_paypal_connection.d.ts +6 -0
  108. package/.medusa/server/src/modules/paypal/migrations/20260115120000_create_paypal_connection.d.ts.map +1 -0
  109. package/.medusa/server/src/modules/paypal/migrations/20260115120000_create_paypal_connection.js +36 -0
  110. package/.medusa/server/src/modules/paypal/migrations/20260115120000_create_paypal_connection.js.map +1 -0
  111. package/.medusa/server/src/modules/paypal/migrations/20260123090000_create_paypal_settings.d.ts +6 -0
  112. package/.medusa/server/src/modules/paypal/migrations/20260123090000_create_paypal_settings.d.ts.map +1 -0
  113. package/.medusa/server/src/modules/paypal/migrations/20260123090000_create_paypal_settings.js +25 -0
  114. package/.medusa/server/src/modules/paypal/migrations/20260123090000_create_paypal_settings.js.map +1 -0
  115. package/.medusa/server/src/modules/paypal/migrations/20260201090000_create_paypal_webhook_event.d.ts +6 -0
  116. package/.medusa/server/src/modules/paypal/migrations/20260201090000_create_paypal_webhook_event.d.ts.map +1 -0
  117. package/.medusa/server/src/modules/paypal/migrations/20260201090000_create_paypal_webhook_event.js +32 -0
  118. package/.medusa/server/src/modules/paypal/migrations/20260201090000_create_paypal_webhook_event.js.map +1 -0
  119. package/.medusa/server/src/modules/paypal/migrations/20260301090000_create_paypal_audit_log.d.ts +6 -0
  120. package/.medusa/server/src/modules/paypal/migrations/20260301090000_create_paypal_audit_log.d.ts.map +1 -0
  121. package/.medusa/server/src/modules/paypal/migrations/20260301090000_create_paypal_audit_log.js +29 -0
  122. package/.medusa/server/src/modules/paypal/migrations/20260301090000_create_paypal_audit_log.js.map +1 -0
  123. package/.medusa/server/src/modules/paypal/migrations/20260401090000_create_paypal_metric.d.ts +6 -0
  124. package/.medusa/server/src/modules/paypal/migrations/20260401090000_create_paypal_metric.d.ts.map +1 -0
  125. package/.medusa/server/src/modules/paypal/migrations/20260401090000_create_paypal_metric.js +30 -0
  126. package/.medusa/server/src/modules/paypal/migrations/20260401090000_create_paypal_metric.js.map +1 -0
  127. package/.medusa/server/src/modules/paypal/migrations/20260501090000_create_paypal_dispute.d.ts +6 -0
  128. package/.medusa/server/src/modules/paypal/migrations/20260501090000_create_paypal_dispute.d.ts.map +1 -0
  129. package/.medusa/server/src/modules/paypal/migrations/20260501090000_create_paypal_dispute.js +43 -0
  130. package/.medusa/server/src/modules/paypal/migrations/20260501090000_create_paypal_dispute.js.map +1 -0
  131. package/.medusa/server/src/modules/paypal/migrations/20260701090000_add_paypal_webhook_event_processing.d.ts +6 -0
  132. package/.medusa/server/src/modules/paypal/migrations/20260701090000_add_paypal_webhook_event_processing.d.ts.map +1 -0
  133. package/.medusa/server/src/modules/paypal/migrations/20260701090000_add_paypal_webhook_event_processing.js +34 -0
  134. package/.medusa/server/src/modules/paypal/migrations/20260701090000_add_paypal_webhook_event_processing.js.map +1 -0
  135. package/.medusa/server/src/modules/paypal/models/paypal_audit_log.d.ts +7 -0
  136. package/.medusa/server/src/modules/paypal/models/paypal_audit_log.d.ts.map +1 -0
  137. package/.medusa/server/src/modules/paypal/models/paypal_audit_log.js +10 -0
  138. package/.medusa/server/src/modules/paypal/models/paypal_audit_log.js.map +1 -0
  139. package/.medusa/server/src/modules/paypal/models/paypal_connection.d.ts +14 -0
  140. package/.medusa/server/src/modules/paypal/models/paypal_connection.d.ts.map +1 -0
  141. package/.medusa/server/src/modules/paypal/models/paypal_connection.js +17 -0
  142. package/.medusa/server/src/modules/paypal/models/paypal_connection.js.map +1 -0
  143. package/.medusa/server/src/modules/paypal/models/paypal_dispute.d.ts +16 -0
  144. package/.medusa/server/src/modules/paypal/models/paypal_dispute.d.ts.map +1 -0
  145. package/.medusa/server/src/modules/paypal/models/paypal_dispute.js +19 -0
  146. package/.medusa/server/src/modules/paypal/models/paypal_dispute.js.map +1 -0
  147. package/.medusa/server/src/modules/paypal/models/paypal_metric.d.ts +7 -0
  148. package/.medusa/server/src/modules/paypal/models/paypal_metric.d.ts.map +1 -0
  149. package/.medusa/server/src/modules/paypal/models/paypal_metric.js +10 -0
  150. package/.medusa/server/src/modules/paypal/models/paypal_metric.js.map +1 -0
  151. package/.medusa/server/src/modules/paypal/models/paypal_settings.d.ts +6 -0
  152. package/.medusa/server/src/modules/paypal/models/paypal_settings.d.ts.map +1 -0
  153. package/.medusa/server/src/modules/paypal/models/paypal_settings.js +9 -0
  154. package/.medusa/server/src/modules/paypal/models/paypal_settings.js.map +1 -0
  155. package/.medusa/server/src/modules/paypal/models/paypal_webhook_event.d.ts +17 -0
  156. package/.medusa/server/src/modules/paypal/models/paypal_webhook_event.d.ts.map +1 -0
  157. package/.medusa/server/src/modules/paypal/models/paypal_webhook_event.js +20 -0
  158. package/.medusa/server/src/modules/paypal/models/paypal_webhook_event.js.map +1 -0
  159. package/.medusa/server/src/modules/paypal/payment-provider/card-service.d.ts +35 -0
  160. package/.medusa/server/src/modules/paypal/payment-provider/card-service.d.ts.map +1 -0
  161. package/.medusa/server/src/modules/paypal/payment-provider/card-service.js +569 -0
  162. package/.medusa/server/src/modules/paypal/payment-provider/card-service.js.map +1 -0
  163. package/.medusa/server/src/modules/paypal/payment-provider/index.d.ts +10 -0
  164. package/.medusa/server/src/modules/paypal/payment-provider/index.d.ts.map +1 -0
  165. package/.medusa/server/src/modules/paypal/payment-provider/index.js +22 -0
  166. package/.medusa/server/src/modules/paypal/payment-provider/index.js.map +1 -0
  167. package/.medusa/server/src/modules/paypal/payment-provider/service.d.ts +44 -0
  168. package/.medusa/server/src/modules/paypal/payment-provider/service.d.ts.map +1 -0
  169. package/.medusa/server/src/modules/paypal/payment-provider/service.js +825 -0
  170. package/.medusa/server/src/modules/paypal/payment-provider/service.js.map +1 -0
  171. package/.medusa/server/src/modules/paypal/payment-provider/webhook-utils.d.ts +3 -0
  172. package/.medusa/server/src/modules/paypal/payment-provider/webhook-utils.d.ts.map +1 -0
  173. package/.medusa/server/src/modules/paypal/payment-provider/webhook-utils.js +74 -0
  174. package/.medusa/server/src/modules/paypal/payment-provider/webhook-utils.js.map +1 -0
  175. package/.medusa/server/src/modules/paypal/service.d.ts +362 -0
  176. package/.medusa/server/src/modules/paypal/service.d.ts.map +1 -0
  177. package/.medusa/server/src/modules/paypal/service.js +1180 -0
  178. package/.medusa/server/src/modules/paypal/service.js.map +1 -0
  179. package/.medusa/server/src/modules/paypal/types/config.d.ts +14 -0
  180. package/.medusa/server/src/modules/paypal/types/config.d.ts.map +1 -0
  181. package/.medusa/server/src/modules/paypal/types/config.js +33 -0
  182. package/.medusa/server/src/modules/paypal/types/config.js.map +1 -0
  183. package/.medusa/server/src/modules/paypal/utils/amounts.d.ts +3 -0
  184. package/.medusa/server/src/modules/paypal/utils/amounts.d.ts.map +1 -0
  185. package/.medusa/server/src/modules/paypal/utils/amounts.js +40 -0
  186. package/.medusa/server/src/modules/paypal/utils/amounts.js.map +1 -0
  187. package/.medusa/server/src/modules/paypal/utils/crypto.d.ts +4 -0
  188. package/.medusa/server/src/modules/paypal/utils/crypto.d.ts.map +1 -0
  189. package/.medusa/server/src/modules/paypal/utils/crypto.js +47 -0
  190. package/.medusa/server/src/modules/paypal/utils/crypto.js.map +1 -0
  191. package/.medusa/server/src/modules/paypal/utils/currencies.d.ts +19 -0
  192. package/.medusa/server/src/modules/paypal/utils/currencies.d.ts.map +1 -0
  193. package/.medusa/server/src/modules/paypal/utils/currencies.js +69 -0
  194. package/.medusa/server/src/modules/paypal/utils/currencies.js.map +1 -0
  195. package/.medusa/server/src/modules/paypal/utils/provider-ids.d.ts +9 -0
  196. package/.medusa/server/src/modules/paypal/utils/provider-ids.d.ts.map +1 -0
  197. package/.medusa/server/src/modules/paypal/utils/provider-ids.js +50 -0
  198. package/.medusa/server/src/modules/paypal/utils/provider-ids.js.map +1 -0
  199. package/.medusa/server/src/modules/paypal/webhook-processor.d.ts +38 -0
  200. package/.medusa/server/src/modules/paypal/webhook-processor.d.ts.map +1 -0
  201. package/.medusa/server/src/modules/paypal/webhook-processor.js +265 -0
  202. package/.medusa/server/src/modules/paypal/webhook-processor.js.map +1 -0
  203. package/LICENSE +21 -0
  204. package/README.md +67 -0
  205. package/package.json +61 -0
  206. package/postcss.config.cjs +3 -0
  207. package/src/admin/index.ts +7 -0
  208. package/src/admin/routes/settings/paypal/_components/Tabs.tsx +55 -0
  209. package/src/admin/routes/settings/paypal/_components/Toast.tsx +51 -0
  210. package/src/admin/routes/settings/paypal/additional-settings/page.tsx +346 -0
  211. package/src/admin/routes/settings/paypal/advanced-card-payments/page.tsx +381 -0
  212. package/src/admin/routes/settings/paypal/apple-pay/page.tsx +5 -0
  213. package/src/admin/routes/settings/paypal/audit-logs/page.tsx +131 -0
  214. package/src/admin/routes/settings/paypal/connection/page.tsx +750 -0
  215. package/src/admin/routes/settings/paypal/disputes/page.tsx +259 -0
  216. package/src/admin/routes/settings/paypal/google-pay/page.tsx +5 -0
  217. package/src/admin/routes/settings/paypal/page.tsx +16 -0
  218. package/src/admin/routes/settings/paypal/pay-later-messaging/page.tsx +5 -0
  219. package/src/admin/routes/settings/paypal/paypal-settings/page.tsx +557 -0
  220. package/src/admin/routes/settings/paypal/reconciliation-status/page.tsx +165 -0
  221. package/src/api/admin/payment-collections/[id]/payment-sessions/route.ts +32 -0
  222. package/src/api/admin/paypal/audit-logs/route.ts +13 -0
  223. package/src/api/admin/paypal/disconnect/route.ts +8 -0
  224. package/src/api/admin/paypal/disputes/[id]/route.ts +19 -0
  225. package/src/api/admin/paypal/disputes/route.ts +30 -0
  226. package/src/api/admin/paypal/disputes/summary/route.ts +18 -0
  227. package/src/api/admin/paypal/environment/route.ts +25 -0
  228. package/src/api/admin/paypal/onboard-complete/route.ts +44 -0
  229. package/src/api/admin/paypal/onboarding-link/route.ts +45 -0
  230. package/src/api/admin/paypal/onboarding-status/route.ts +18 -0
  231. package/src/api/admin/paypal/reconciliation-status/route.ts +7 -0
  232. package/src/api/admin/paypal/rotate-credentials/route.ts +8 -0
  233. package/src/api/admin/paypal/save-credentials/route.ts +14 -0
  234. package/src/api/admin/paypal/settings/route.ts +14 -0
  235. package/src/api/admin/paypal/status/route.ts +12 -0
  236. package/src/api/store/payment-collections/[id]/payment-sessions/route.ts +51 -0
  237. package/src/api/store/paypal/capture-order/route.ts +270 -0
  238. package/src/api/store/paypal/config/route.ts +59 -0
  239. package/src/api/store/paypal/create-order/route.ts +374 -0
  240. package/src/api/store/paypal/disputes/route.ts +67 -0
  241. package/src/api/store/paypal/settings/route.ts +12 -0
  242. package/src/api/store/paypal/webhook/route.ts +247 -0
  243. package/src/jobs/paypal-reconcile.ts +135 -0
  244. package/src/jobs/paypal-webhook-retry.ts +86 -0
  245. package/src/modules/paypal/clients/paypal-seller.client.ts +59 -0
  246. package/src/modules/paypal/index.ts +8 -0
  247. package/src/modules/paypal/migrations/20260115120000_create_paypal_connection.ts +33 -0
  248. package/src/modules/paypal/migrations/20260123090000_create_paypal_settings.ts +22 -0
  249. package/src/modules/paypal/migrations/20260201090000_create_paypal_webhook_event.ts +29 -0
  250. package/src/modules/paypal/migrations/20260301090000_create_paypal_audit_log.ts +26 -0
  251. package/src/modules/paypal/migrations/20260401090000_create_paypal_metric.ts +27 -0
  252. package/src/modules/paypal/migrations/20260501090000_create_paypal_dispute.ts +40 -0
  253. package/src/modules/paypal/migrations/20260701090000_add_paypal_webhook_event_processing.ts +31 -0
  254. package/src/modules/paypal/models/paypal_audit_log.ts +9 -0
  255. package/src/modules/paypal/models/paypal_connection.ts +21 -0
  256. package/src/modules/paypal/models/paypal_dispute.ts +18 -0
  257. package/src/modules/paypal/models/paypal_metric.ts +9 -0
  258. package/src/modules/paypal/models/paypal_settings.ts +8 -0
  259. package/src/modules/paypal/models/paypal_webhook_event.ts +19 -0
  260. package/src/modules/paypal/payment-provider/README.md +22 -0
  261. package/src/modules/paypal/payment-provider/card-service.ts +710 -0
  262. package/src/modules/paypal/payment-provider/index.ts +19 -0
  263. package/src/modules/paypal/payment-provider/service.ts +1035 -0
  264. package/src/modules/paypal/payment-provider/webhook-utils.ts +88 -0
  265. package/src/modules/paypal/service.ts +1422 -0
  266. package/src/modules/paypal/types/config.ts +47 -0
  267. package/src/modules/paypal/utils/amounts.ts +41 -0
  268. package/src/modules/paypal/utils/crypto.ts +51 -0
  269. package/src/modules/paypal/utils/currencies.ts +84 -0
  270. package/src/modules/paypal/utils/provider-ids.ts +53 -0
  271. package/src/modules/paypal/webhook-processor.ts +313 -0
  272. package/tsconfig.json +31 -0
@@ -0,0 +1,750 @@
1
+ import React, {
2
+ useCallback,
3
+ useEffect,
4
+ useLayoutEffect,
5
+ useMemo,
6
+ useRef,
7
+ useState,
8
+ } from "react"
9
+ import { defineRouteConfig } from "@medusajs/admin-sdk"
10
+ import PayPalTabs from "../_components/Tabs"
11
+
12
+ export const config = defineRouteConfig({
13
+ label: "PayPal Connection",
14
+ hide: true,
15
+ })
16
+
17
+ /* ------------------------------------------------------------------ */
18
+ /* HIGH PRIORITY EXECUTION - RUNS BEFORE COMPONENT MOUNTS */
19
+ /* ------------------------------------------------------------------ */
20
+
21
+ // 1. Inject Preload Link IMMEDIATELY
22
+ if (typeof window !== "undefined") {
23
+ const preloadHref =
24
+ "https://www.paypal.com/webapps/merchantboarding/js/lib/lightbox/partner.js"
25
+
26
+ const existingPreload = document.head.querySelector(
27
+ `link[rel="preload"][href="${preloadHref}"]`
28
+ )
29
+ if (!existingPreload) {
30
+ const preloadLink = document.createElement("link")
31
+ preloadLink.rel = "preload"
32
+ preloadLink.href = preloadHref
33
+ preloadLink.as = "script"
34
+ document.head.appendChild(preloadLink)
35
+ }
36
+
37
+ // 2. Inject PayPal Script IMMEDIATELY (before React renders)
38
+ const existingScript = document.getElementById(
39
+ "paypal-partner-js"
40
+ ) as HTMLScriptElement | null
41
+ if (!existingScript) {
42
+ const ppScript = document.createElement("script")
43
+ ppScript.id = "paypal-partner-js"
44
+ ppScript.src = preloadHref
45
+ ppScript.async = true
46
+ document.head.appendChild(ppScript)
47
+ }
48
+ }
49
+
50
+ declare global {
51
+ interface Window {
52
+ PAYPAL?: {
53
+ apps?: {
54
+ Signup?: {
55
+ miniBrowser?: { init: () => void }
56
+ // some partner.js builds expose MiniBrowser (capital M)
57
+ MiniBrowser?: { closeFlow?: () => void }
58
+ }
59
+ }
60
+ }
61
+ onboardingCallback?: (authCode: string, sharedId: string) => void
62
+ }
63
+ }
64
+
65
+ // Partner.js is now loaded lazily only when needed (removed global injection)
66
+
67
+ const SERVICE_URL = "/admin/paypal/onboarding-link"
68
+ const CACHE_KEY = "pp_onboard_cache"
69
+ const RELOAD_KEY = "pp_onboard_reloaded_once"
70
+ const CACHE_EXPIRY = 10 * 60 * 1000 // 10 minutes
71
+
72
+ // ✅ backend endpoint to exchange authCode/sharedId (medusa)
73
+ const ONBOARDING_COMPLETE_ENDPOINT = "/admin/paypal/onboard-complete"
74
+ const STATUS_ENDPOINT = "/admin/paypal/status"
75
+ const SAVE_CREDENTIALS_ENDPOINT = "/admin/paypal/save-credentials"
76
+ const DISCONNECT_ENDPOINT = "/admin/paypal/disconnect"
77
+
78
+ // 3. Immediate Cache Check (runs before React component)
79
+ let cachedUrl: string | null = null
80
+ if (typeof window !== "undefined") {
81
+ try {
82
+ const cached = localStorage.getItem(CACHE_KEY)
83
+ if (cached) {
84
+ const data = JSON.parse(cached)
85
+ if (new Date().getTime() - data.ts < CACHE_EXPIRY) {
86
+ cachedUrl = data.url
87
+ }
88
+ }
89
+ } catch (e) {
90
+ console.error("Cache read error:", e)
91
+ }
92
+ }
93
+
94
+ /* ------------------------------------------------------------------ */
95
+ /* REACT COMPONENT */
96
+ /* ------------------------------------------------------------------ */
97
+
98
+ export default function PayPalConnectionPage() {
99
+ const [env, setEnv] = useState<"sandbox" | "live">("sandbox")
100
+
101
+ // Sync initial environment from backend
102
+ useEffect(() => {
103
+ fetch("/admin/paypal/environment", { method: "GET" })
104
+ .then((r) => r.json())
105
+ .then((d) => {
106
+ const v = d?.environment === "live" ? "live" : "sandbox"
107
+ setEnv(v)
108
+ })
109
+ .catch(() => {})
110
+ }, [])
111
+ const [connState, setConnState] = useState<
112
+ "loading" | "ready" | "connected" | "error"
113
+ >("loading")
114
+ const [error, setError] = useState<string | null>(null)
115
+ const [finalUrl, setFinalUrl] = useState<string>("")
116
+ const [showManual, setShowManual] = useState(false)
117
+ const [clientId, setClientId] = useState("")
118
+ const [secret, setSecret] = useState("")
119
+ const [merchantId, setMerchantId] = useState("")
120
+ const [statusInfo, setStatusInfo] = useState<{
121
+ seller_client_id_masked?: string | null
122
+ seller_client_secret_masked?: string | null
123
+ seller_email?: string | null
124
+ } | null>(null)
125
+
126
+ // ✅ onboarding in progress (for disabling UI)
127
+ const [onboardingInProgress, setOnboardingInProgress] = useState(false)
128
+
129
+ const initLoaderRef = useRef<HTMLDivElement>(null)
130
+ const paypalButtonRef = useRef<HTMLAnchorElement>(null)
131
+ const errorLogRef = useRef<HTMLDivElement>(null)
132
+ const runIdRef = useRef(0)
133
+ const currentRunId = useRef(0)
134
+
135
+ // Measure PayPal button width for OR centering (Woo-style)
136
+ const ppBtnMeasureRef = useRef<HTMLAnchorElement | null>(null)
137
+ const [ppBtnWidth, setPpBtnWidth] = useState<number | null>(null)
138
+
139
+ const canSaveManual = useMemo(() => {
140
+ return clientId.trim().length > 0 && secret.trim().length > 0
141
+ }, [clientId, secret])
142
+
143
+ const maskValue = useCallback((value: string, visibleChars = 4) => {
144
+ if (!value) return ""
145
+ if (value.length <= visibleChars) {
146
+ return "•".repeat(value.length)
147
+ }
148
+ return `${"•".repeat(Math.max(0, value.length - visibleChars))}${value.slice(
149
+ -visibleChars
150
+ )}`
151
+ }, [])
152
+
153
+ // Exact copy of fetchFreshLink from original JS (+ reload-once logic ONLY)
154
+ const fetchFreshLink = useCallback(
155
+ (runId: number) => {
156
+ if (initLoaderRef.current) {
157
+ const loaderText = initLoaderRef.current.querySelector("#loader-text")
158
+ if (loaderText)
159
+ loaderText.textContent = "Generating onboarding session..."
160
+ }
161
+
162
+ fetch(SERVICE_URL, {
163
+ method: "POST",
164
+ headers: { "content-type": "application/json" },
165
+ body: JSON.stringify({
166
+ products: ["PPCP"],
167
+ }),
168
+ })
169
+ .then((r) => r.json())
170
+ .then((data) => {
171
+ if (runId !== currentRunId.current) return
172
+
173
+ const href = data?.onboarding_url
174
+ if (!href) {
175
+ showError("Onboarding URL not returned.")
176
+ return
177
+ }
178
+
179
+ const finalUrl =
180
+ href + (href.includes("?") ? "&" : "?") + "displayMode=minibrowser"
181
+
182
+ localStorage.setItem(
183
+ CACHE_KEY,
184
+ JSON.stringify({
185
+ url: finalUrl,
186
+ ts: Date.now(),
187
+ })
188
+ )
189
+
190
+ if (!localStorage.getItem(RELOAD_KEY)) {
191
+ localStorage.setItem(RELOAD_KEY, "1")
192
+ window.location.reload()
193
+ return
194
+ }
195
+
196
+ activatePayPal(finalUrl, runId)
197
+ })
198
+ .catch(() => {
199
+ if (runId !== currentRunId.current) return
200
+ showError("Unable to connect to service.")
201
+ })
202
+ },
203
+ [env]
204
+ )
205
+
206
+ // Exact copy of showUI from original JS (safe: only init when button exists)
207
+ const showUI = useCallback(() => {
208
+ const btn = document.querySelector('[data-paypal-button="true"]')
209
+ if (btn && window.PAYPAL?.apps?.Signup?.miniBrowser?.init) {
210
+ window.PAYPAL.apps.Signup.miniBrowser.init()
211
+ }
212
+ setConnState("ready")
213
+ }, [])
214
+
215
+ // Exact copy of showError from original JS
216
+ const showError = useCallback((msg: string) => {
217
+ setConnState("error")
218
+ setError(msg)
219
+ }, [])
220
+
221
+ // activatePayPal (keeps PayPal partner.js global injection; waits for PAYPAL then inits)
222
+ const activatePayPal = useCallback(
223
+ (url: string, runId: number) => {
224
+ if (paypalButtonRef.current) {
225
+ paypalButtonRef.current.href = url
226
+ }
227
+ setFinalUrl(url)
228
+
229
+ const tryInit = () => {
230
+ if (runId !== currentRunId.current) return
231
+ if (window.PAYPAL?.apps?.Signup) {
232
+ showUI()
233
+ return
234
+ }
235
+ // wait briefly for partner.js to finish loading
236
+ setTimeout(tryInit, 50)
237
+ }
238
+
239
+ tryInit()
240
+ },
241
+ [showUI]
242
+ )
243
+
244
+ // Initialize - runs on mount and env change
245
+ useEffect(() => {
246
+ currentRunId.current = ++runIdRef.current
247
+ const runId = currentRunId.current
248
+
249
+ let cancelled = false
250
+
251
+ const run = async () => {
252
+ setConnState("loading")
253
+ setError(null)
254
+ setFinalUrl("")
255
+
256
+ // 1) If already connected in DB, don't ask to onboard again
257
+ try {
258
+ const r = await fetch(`${STATUS_ENDPOINT}?environment=${env}`, {
259
+ method: "GET",
260
+ })
261
+ const st = await r.json().catch(() => ({}))
262
+
263
+ if (cancelled || runId !== currentRunId.current) return
264
+
265
+ setStatusInfo(st)
266
+
267
+ const isConnected =
268
+ st?.status === "connected" && st?.seller_client_id_present === true
269
+
270
+ if (isConnected) {
271
+ setConnState("connected")
272
+ setShowManual(false)
273
+ return
274
+ }
275
+ } catch (e) {
276
+ // ignore status errors; proceed with onboarding
277
+ console.error(e)
278
+ }
279
+
280
+ // 2) Not connected -> continue onboarding flow
281
+ if (cachedUrl) {
282
+ console.log("Using prioritized cache...")
283
+ activatePayPal(cachedUrl, runId)
284
+ } else {
285
+ fetchFreshLink(runId)
286
+ }
287
+ }
288
+
289
+ run()
290
+
291
+ return () => {
292
+ cancelled = true
293
+ currentRunId.current = 0
294
+ }
295
+ }, [env, fetchFreshLink, activatePayPal])
296
+
297
+ // ✅ setupOnboarding() behavior (Woo-style) inside callback
298
+ useLayoutEffect(() => {
299
+ window.onboardingCallback = async function (authCode: string, sharedId: string) {
300
+ // Woo sets this; keep safe
301
+ try {
302
+ ;(window as any).onbeforeunload = ""
303
+ } catch {}
304
+
305
+ // show blocking state (no new UI components, just disable + loader text)
306
+ setOnboardingInProgress(true)
307
+ setConnState("loading")
308
+ setError(null)
309
+
310
+ // post to backend (authCode/sharedId/env)
311
+ const payload = {
312
+ authCode,
313
+ sharedId,
314
+ env: env === "sandbox" ? "sandbox" : "live",
315
+ }
316
+
317
+ try {
318
+ const res = await fetch(ONBOARDING_COMPLETE_ENDPOINT, {
319
+ method: "POST",
320
+ headers: { "content-type": "application/json" },
321
+ body: JSON.stringify(payload),
322
+ })
323
+
324
+ if (!res.ok) {
325
+ const txt = await res.text().catch(() => "")
326
+ throw new Error(txt || `Onboarding exchange failed (${res.status})`)
327
+ }
328
+
329
+ // ✅ ONLY NOW close the mini-browser flow
330
+ try {
331
+ const close1 = window.PAYPAL?.apps?.Signup?.MiniBrowser?.closeFlow
332
+ if (typeof close1 === "function") close1()
333
+ } catch {}
334
+ try {
335
+ const close2 =
336
+ window.PAYPAL?.apps?.Signup?.miniBrowser &&
337
+ (window.PAYPAL.apps.Signup.miniBrowser as any).closeFlow
338
+ if (typeof close2 === "function") close2()
339
+ } catch {}
340
+
341
+ // clear cache so next run fetches new url
342
+ try {
343
+ localStorage.removeItem(CACHE_KEY)
344
+ localStorage.removeItem(RELOAD_KEY)
345
+ } catch {}
346
+
347
+ // Woo does: window.location.href = window.location.href;
348
+ window.location.href = window.location.href
349
+ } catch (e: any) {
350
+ console.error(e)
351
+ setConnState("error")
352
+ setError(e?.message || "Exchange failed while saving credentials.")
353
+ setOnboardingInProgress(false)
354
+ }
355
+ }
356
+
357
+ return () => {
358
+ window.onboardingCallback = undefined
359
+ }
360
+ }, [env])
361
+
362
+ // Measure PayPal button width (for OR centering under button)
363
+ useLayoutEffect(() => {
364
+ const el = ppBtnMeasureRef.current
365
+ if (!el) return
366
+
367
+ const update = () => {
368
+ const w = Math.round(el.getBoundingClientRect().width || 0)
369
+ if (w > 0) setPpBtnWidth(w)
370
+ }
371
+
372
+ update()
373
+
374
+ let ro: ResizeObserver | null = null
375
+ if (typeof ResizeObserver !== "undefined") {
376
+ ro = new ResizeObserver(() => update())
377
+ ro.observe(el)
378
+ } else {
379
+ window.addEventListener("resize", update)
380
+ }
381
+
382
+ return () => {
383
+ if (ro) ro.disconnect()
384
+ else window.removeEventListener("resize", update)
385
+ }
386
+ }, [connState, env, finalUrl])
387
+
388
+ const handleConnectClick = (e: React.MouseEvent<HTMLAnchorElement>) => {
389
+ if (connState !== "ready" || !finalUrl || onboardingInProgress) {
390
+ e.preventDefault()
391
+ }
392
+ }
393
+
394
+ const handleSaveManual = async () => {
395
+ if (!canSaveManual || onboardingInProgress) return
396
+ setOnboardingInProgress(true)
397
+ setConnState("loading")
398
+ setError(null)
399
+
400
+ try {
401
+ const res = await fetch(SAVE_CREDENTIALS_ENDPOINT, {
402
+ method: "POST",
403
+ headers: { "content-type": "application/json" },
404
+ body: JSON.stringify({
405
+ clientId: clientId.trim(),
406
+ clientSecret: secret.trim(),
407
+ }),
408
+ })
409
+
410
+ if (!res.ok) {
411
+ const txt = await res.text().catch(() => "")
412
+ throw new Error(txt || `Save credentials failed (${res.status})`)
413
+ }
414
+
415
+ setConnState("connected")
416
+ setStatusInfo({
417
+ seller_client_id_masked: maskValue(clientId.trim()),
418
+ seller_client_secret_masked: "••••••••",
419
+ })
420
+ setShowManual(false)
421
+
422
+ try {
423
+ localStorage.removeItem(CACHE_KEY)
424
+ localStorage.removeItem(RELOAD_KEY)
425
+ } catch {}
426
+ } catch (e: any) {
427
+ console.error(e)
428
+ setConnState("error")
429
+ setError(e?.message || "Failed to save credentials.")
430
+ } finally {
431
+ setOnboardingInProgress(false)
432
+ }
433
+ }
434
+
435
+ const handleDisconnect = async () => {
436
+ if (onboardingInProgress) return
437
+ if (!window.confirm("Disconnect PayPal for this environment?")) return
438
+
439
+ setOnboardingInProgress(true)
440
+ setConnState("loading")
441
+ setError(null)
442
+ setFinalUrl("")
443
+ setShowManual(false)
444
+
445
+ try {
446
+ const res = await fetch(DISCONNECT_ENDPOINT, {
447
+ method: "POST",
448
+ headers: { "content-type": "application/json" },
449
+ body: JSON.stringify({ environment: env }),
450
+ })
451
+
452
+ if (!res.ok) {
453
+ const t = await res.text().catch(() => "")
454
+ throw new Error(t || `Disconnect failed (${res.status})`)
455
+ }
456
+
457
+ try {
458
+ localStorage.removeItem(CACHE_KEY)
459
+ localStorage.removeItem(RELOAD_KEY)
460
+ } catch {}
461
+
462
+ // Restart onboarding link generation for current env
463
+ currentRunId.current = ++runIdRef.current
464
+ const runId = currentRunId.current
465
+ fetchFreshLink(runId)
466
+ } catch (e: any) {
467
+ console.error(e)
468
+ setConnState("error")
469
+ setError(e?.message || "Failed to disconnect.")
470
+ } finally {
471
+ setOnboardingInProgress(false)
472
+ }
473
+ }
474
+
475
+ const handleEnvChange = async (e: React.ChangeEvent<HTMLSelectElement>) => {
476
+ const next = e.target.value as "sandbox" | "live"
477
+ setEnv(next)
478
+ cachedUrl = null
479
+
480
+ try {
481
+ await fetch("/admin/paypal/environment", {
482
+ method: "POST",
483
+ headers: { "content-type": "application/json" },
484
+ body: JSON.stringify({ environment: next }),
485
+ })
486
+ } catch {}
487
+
488
+ try {
489
+ localStorage.removeItem(CACHE_KEY)
490
+ localStorage.removeItem(RELOAD_KEY)
491
+ } catch {}
492
+ }
493
+
494
+ return (
495
+ <div className="p-6">
496
+ <div className="flex flex-col gap-6">
497
+ <h1 className="text-xl font-semibold">PayPal Gateway By Easy Payment</h1>
498
+
499
+ {/* Tabs header */}
500
+ <PayPalTabs />
501
+
502
+ {/* Main container */}
503
+ <div className="rounded-md border border-ui-border-base p-4 shadow-sm">
504
+ <div className="grid grid-cols-1 gap-y-6 md:grid-cols-[260px_1fr] md:items-start">
505
+ {/* Environment */}
506
+ <div className="text-sm font-medium pt-2">Environment</div>
507
+ <div className="max-w-xl">
508
+ <select
509
+ value={env}
510
+ onChange={handleEnvChange}
511
+ disabled={onboardingInProgress}
512
+ className="w-full rounded-md border border-ui-border-base bg-transparent px-3 py-2 text-sm"
513
+ >
514
+ <option value="sandbox">Sandbox (Test Mode)</option>
515
+ <option value="live">Live (Production)</option>
516
+ </select>
517
+ </div>
518
+
519
+ {/* Connect */}
520
+ <div className="text-sm font-medium pt-2">
521
+ {env === "sandbox" ? "Connect to PayPal Sandbox" : "Connect to PayPal Live"}
522
+ </div>
523
+
524
+ <div className="max-w-xl">
525
+ {connState === "connected" ? (
526
+ <div>
527
+ <div className="text-sm text-green-600 bg-green-50 p-3 rounded border border-green-200">
528
+ ✅ Successfully connected to PayPal!
529
+ {/* Hidden PayPal button anchor to satisfy partner.js DOM expectations */}
530
+ <a
531
+ data-paypal-button="true"
532
+ data-paypal-onboard-complete="onboardingCallback"
533
+ href="#"
534
+ style={{ display: "none" }}
535
+ >
536
+ PayPal
537
+ </a>
538
+ </div>
539
+ <div className="mt-3 rounded-md border border-ui-border-base bg-ui-bg-subtle p-3 text-xs text-ui-fg-subtle">
540
+ <div className="font-medium text-ui-fg-base">
541
+ Connected PayPal account
542
+ </div>
543
+ <div className="mt-1">
544
+ Email:{" "}
545
+ <span className="font-mono text-ui-fg-base">
546
+ {statusInfo?.seller_email || "Unavailable"}
547
+ </span>
548
+ </div>
549
+ </div>
550
+ <div className="mt-3 flex items-center gap-2">
551
+ <button
552
+ type="button"
553
+ onClick={handleDisconnect}
554
+ disabled={onboardingInProgress}
555
+ className="rounded-md border border-ui-border-base px-3 py-2 text-sm font-medium hover:bg-ui-bg-subtle disabled:opacity-50 disabled:cursor-not-allowed"
556
+ >
557
+ Disconnect
558
+ </button>
559
+ </div>
560
+ </div>
561
+ ) : (
562
+ <>
563
+ {/* Status Loader */}
564
+ <div
565
+ ref={initLoaderRef}
566
+ id="init-loader"
567
+ className={`status-msg mb-4 ${
568
+ connState !== "loading" ? "hidden" : "block"
569
+ }`}
570
+ >
571
+ <div className="loader inline-block align-middle mr-2"></div>
572
+ <span id="loader-text" className="text-sm">
573
+ {onboardingInProgress
574
+ ? "Configuring connection to PayPal…"
575
+ : "Checking connection..."}
576
+ </span>
577
+ </div>
578
+
579
+ {/* PayPal Button */}
580
+ <div className={`${connState === "ready" ? "block" : "hidden"}`}>
581
+ {/* Row 1: button (left aligned) */}
582
+ <a
583
+ ref={(node) => {
584
+ paypalButtonRef.current = node
585
+ ppBtnMeasureRef.current = node
586
+ }}
587
+ id="paypal-button"
588
+ data-paypal-button="true"
589
+ href={finalUrl || "#"}
590
+ data-paypal-onboard-complete="onboardingCallback"
591
+ onClick={handleConnectClick}
592
+ className="btn-paypal"
593
+ style={{
594
+ borderRadius: "50px",
595
+ textDecoration: "none",
596
+ display: "inline-block",
597
+ fontWeight: "bold",
598
+ border: "none",
599
+ cursor: onboardingInProgress ? "not-allowed" : "pointer",
600
+ opacity: onboardingInProgress ? 0.6 : 1,
601
+ pointerEvents: onboardingInProgress ? "none" : "auto",
602
+ }}
603
+ >
604
+ Connect to PayPal
605
+ </a>
606
+
607
+ {/* Row 2: OR centered under button width */}
608
+ <div
609
+ className="mt-2"
610
+ style={{
611
+ width: ppBtnWidth ? `${ppBtnWidth}px` : "auto",
612
+ marginTop: "20px",
613
+ marginBottom: "10px",
614
+ }}
615
+ >
616
+ <div className="flex justify-center">
617
+ <span className="text-[11px] text-ui-fg-muted leading-none">
618
+ OR
619
+ </span>
620
+ </div>
621
+ </div>
622
+
623
+ {/* Row 3: manual link aligned to button LEFT */}
624
+ <div className="mt-1">
625
+ <button
626
+ type="button"
627
+ onClick={() => setShowManual(!showManual)}
628
+ disabled={onboardingInProgress}
629
+ className="text-sm text-ui-fg-interactive underline whitespace-nowrap disabled:opacity-50 disabled:cursor-not-allowed"
630
+ >
631
+ Click here to insert credentials manually
632
+ </button>
633
+ </div>
634
+ </div>
635
+
636
+ {/* If not ready yet, show manual link still */}
637
+ <div className={`${connState === "ready" ? "hidden" : "block"} mt-3`}>
638
+ <button
639
+ type="button"
640
+ onClick={() => setShowManual(!showManual)}
641
+ disabled={onboardingInProgress}
642
+ className="text-sm text-ui-fg-interactive underline whitespace-nowrap disabled:opacity-50 disabled:cursor-not-allowed"
643
+ >
644
+ Click here to insert credentials manually
645
+ </button>
646
+ </div>
647
+
648
+ {/* Error Log */}
649
+ <div
650
+ ref={errorLogRef}
651
+ id="error-log"
652
+ className={`mt-4 text-left text-xs bg-red-50 text-red-600 p-3 border border-red-200 rounded ${
653
+ connState === "error" && error ? "block" : "hidden"
654
+ }`}
655
+ >
656
+ {error}
657
+ </div>
658
+ </>
659
+ )}
660
+ </div>
661
+
662
+ {/* Manual credentials section */}
663
+ {showManual && (
664
+ <div className="md:col-span-2">
665
+ <div className="ml-[260px] max-w-xl mt-4 grid grid-cols-1 gap-3 md:grid-cols-2">
666
+ <div className="flex flex-col gap-1">
667
+ <label className="text-sm font-medium">Client ID</label>
668
+ <input
669
+ type="text"
670
+ value={clientId}
671
+ onChange={(e) => setClientId(e.target.value)}
672
+ disabled={onboardingInProgress}
673
+ className="rounded-md border border-ui-border-base bg-transparent px-3 py-2 text-sm disabled:opacity-50"
674
+ placeholder={
675
+ env === "sandbox" ? "Sandbox Client ID" : "Live Client ID"
676
+ }
677
+ />
678
+ </div>
679
+
680
+ <div className="flex flex-col gap-1">
681
+ <label className="text-sm font-medium">Client Secret</label>
682
+ <input
683
+ type="password"
684
+ value={secret}
685
+ onChange={(e) => setSecret(e.target.value)}
686
+ disabled={onboardingInProgress}
687
+ className="rounded-md border border-ui-border-base bg-transparent px-3 py-2 text-sm disabled:opacity-50"
688
+ placeholder={env === "sandbox" ? "Sandbox Secret" : "Live Secret"}
689
+ />
690
+ </div>
691
+
692
+ <div className="flex flex-col gap-1 md:col-span-2">
693
+ <label className="text-sm font-medium">Merchant ID (optional)</label>
694
+ <input
695
+ type="text"
696
+ value={merchantId}
697
+ onChange={(e) => setMerchantId(e.target.value)}
698
+ disabled={onboardingInProgress}
699
+ className="rounded-md border border-ui-border-base bg-transparent px-3 py-2 text-sm disabled:opacity-50"
700
+ placeholder="Merchant ID"
701
+ />
702
+ </div>
703
+
704
+ <div className="md:col-span-2 flex items-center gap-2 mt-2">
705
+ <button
706
+ type="button"
707
+ className="rounded-md border border-ui-border-base px-3 py-2 text-sm font-medium hover:bg-ui-bg-subtle disabled:opacity-50 disabled:cursor-not-allowed"
708
+ onClick={() => setShowManual(false)}
709
+ disabled={onboardingInProgress}
710
+ >
711
+ Cancel
712
+ </button>
713
+
714
+ <button
715
+ type="button"
716
+ className="rounded-md border border-ui-border-base px-3 py-2 text-sm font-medium bg-ui-bg-base hover:bg-ui-bg-subtle disabled:opacity-50 disabled:cursor-not-allowed"
717
+ disabled={!canSaveManual || onboardingInProgress}
718
+ onClick={handleSaveManual}
719
+ >
720
+ Save credentials
721
+ </button>
722
+ </div>
723
+ </div>
724
+ </div>
725
+ )}
726
+ </div>
727
+ </div>
728
+ </div>
729
+
730
+ {/* Loader styles */}
731
+ <style>{`
732
+ .loader {
733
+ border: 3px solid #f3f3f3;
734
+ border-top: 3px solid #0070ba;
735
+ border-radius: 50%;
736
+ width: 18px;
737
+ height: 18px;
738
+ animation: spin 1s linear infinite;
739
+ display: inline-block;
740
+ vertical-align: middle;
741
+ margin-right: 8px;
742
+ }
743
+ @keyframes spin {
744
+ 0% { transform: rotate(0deg); }
745
+ 100% { transform: rotate(360deg); }
746
+ }
747
+ `}</style>
748
+ </div>
749
+ )
750
+ }