@easypayment/medusa-paypal 0.7.1 → 0.7.3

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 (89) hide show
  1. package/.medusa/server/src/admin/index.js +6 -7
  2. package/.medusa/server/src/admin/index.mjs +6 -7
  3. package/.medusa/server/src/api/admin/paypal/onboard-complete/route.d.ts +0 -4
  4. package/.medusa/server/src/api/admin/paypal/onboard-complete/route.d.ts.map +1 -1
  5. package/.medusa/server/src/api/admin/paypal/onboard-complete/route.js +0 -10
  6. package/.medusa/server/src/api/admin/paypal/onboard-complete/route.js.map +1 -1
  7. package/.medusa/server/src/api/admin/paypal/onboarding-status/route.d.ts.map +1 -1
  8. package/.medusa/server/src/api/admin/paypal/onboarding-status/route.js +0 -1
  9. package/.medusa/server/src/api/admin/paypal/onboarding-status/route.js.map +1 -1
  10. package/.medusa/server/src/api/store/payment-collections/[id]/payment-sessions/route.d.ts.map +1 -1
  11. package/.medusa/server/src/api/store/payment-collections/[id]/payment-sessions/route.js +0 -1
  12. package/.medusa/server/src/api/store/payment-collections/[id]/payment-sessions/route.js.map +1 -1
  13. package/.medusa/server/src/api/store/paypal/capture-order/route.d.ts.map +1 -1
  14. package/.medusa/server/src/api/store/paypal/capture-order/route.js +2 -4
  15. package/.medusa/server/src/api/store/paypal/capture-order/route.js.map +1 -1
  16. package/.medusa/server/src/api/store/paypal/config/route.d.ts.map +1 -1
  17. package/.medusa/server/src/api/store/paypal/config/route.js +1 -4
  18. package/.medusa/server/src/api/store/paypal/config/route.js.map +1 -1
  19. package/.medusa/server/src/api/store/paypal/create-order/route.d.ts.map +1 -1
  20. package/.medusa/server/src/api/store/paypal/create-order/route.js +2 -13
  21. package/.medusa/server/src/api/store/paypal/create-order/route.js.map +1 -1
  22. package/.medusa/server/src/api/store/paypal-complete/route.d.ts.map +1 -1
  23. package/.medusa/server/src/api/store/paypal-complete/route.js +0 -10
  24. package/.medusa/server/src/api/store/paypal-complete/route.js.map +1 -1
  25. package/.medusa/server/src/modules/paypal/clients/paypal-seller.client.d.ts +1 -0
  26. package/.medusa/server/src/modules/paypal/clients/paypal-seller.client.d.ts.map +1 -1
  27. package/.medusa/server/src/modules/paypal/clients/paypal-seller.client.js +2 -0
  28. package/.medusa/server/src/modules/paypal/clients/paypal-seller.client.js.map +1 -1
  29. package/.medusa/server/src/modules/paypal/models/paypal_connection.js +2 -2
  30. package/.medusa/server/src/modules/paypal/models/paypal_connection.js.map +1 -1
  31. package/.medusa/server/src/modules/paypal/payment-provider/card-service.d.ts +0 -3
  32. package/.medusa/server/src/modules/paypal/payment-provider/card-service.d.ts.map +1 -1
  33. package/.medusa/server/src/modules/paypal/payment-provider/card-service.js +8 -4
  34. package/.medusa/server/src/modules/paypal/payment-provider/card-service.js.map +1 -1
  35. package/.medusa/server/src/modules/paypal/payment-provider/index.d.ts +0 -4
  36. package/.medusa/server/src/modules/paypal/payment-provider/index.d.ts.map +1 -1
  37. package/.medusa/server/src/modules/paypal/payment-provider/index.js +0 -4
  38. package/.medusa/server/src/modules/paypal/payment-provider/index.js.map +1 -1
  39. package/.medusa/server/src/modules/paypal/payment-provider/service.d.ts.map +1 -1
  40. package/.medusa/server/src/modules/paypal/payment-provider/service.js +11 -24
  41. package/.medusa/server/src/modules/paypal/payment-provider/service.js.map +1 -1
  42. package/.medusa/server/src/modules/paypal/service.d.ts +1 -0
  43. package/.medusa/server/src/modules/paypal/service.d.ts.map +1 -1
  44. package/.medusa/server/src/modules/paypal/service.js +20 -5
  45. package/.medusa/server/src/modules/paypal/service.js.map +1 -1
  46. package/.medusa/server/src/modules/paypal/types/config.js +2 -2
  47. package/.medusa/server/src/modules/paypal/types/config.js.map +1 -1
  48. package/.medusa/server/src/modules/paypal/utils/paypal-auth.d.ts +0 -4
  49. package/.medusa/server/src/modules/paypal/utils/paypal-auth.d.ts.map +1 -1
  50. package/.medusa/server/src/modules/paypal/utils/paypal-auth.js +2 -4
  51. package/.medusa/server/src/modules/paypal/utils/paypal-auth.js.map +1 -1
  52. package/.medusa/server/src/modules/paypal/webhook-processor.d.ts.map +1 -1
  53. package/.medusa/server/src/modules/paypal/webhook-processor.js +0 -11
  54. package/.medusa/server/src/modules/paypal/webhook-processor.js.map +1 -1
  55. package/.medusa/server/src/providers/paypal/index.d.ts.map +1 -1
  56. package/.medusa/server/src/providers/paypal/index.js +0 -1
  57. package/.medusa/server/src/providers/paypal/index.js.map +1 -1
  58. package/.medusa/server/src/providers/paypal_card/index.d.ts.map +1 -1
  59. package/.medusa/server/src/providers/paypal_card/index.js +0 -1
  60. package/.medusa/server/src/providers/paypal_card/index.js.map +1 -1
  61. package/.medusa/server/src/subscribers/paypal-order-invoice.d.ts.map +1 -1
  62. package/.medusa/server/src/subscribers/paypal-order-invoice.js +0 -16
  63. package/.medusa/server/src/subscribers/paypal-order-invoice.js.map +1 -1
  64. package/README.md +9 -22
  65. package/package.json +7 -4
  66. package/src/admin/routes/settings/paypal/additional-settings/page.tsx +0 -1
  67. package/src/admin/routes/settings/paypal/advanced-card-payments/page.tsx +0 -1
  68. package/src/admin/routes/settings/paypal/connection/page.tsx +1 -49
  69. package/src/admin/routes/settings/paypal/page.tsx +13 -17
  70. package/src/admin/routes/settings/paypal/paypal-settings/page.tsx +0 -2
  71. package/src/api/admin/paypal/onboard-complete/route.ts +34 -44
  72. package/src/api/admin/paypal/onboarding-status/route.ts +17 -18
  73. package/src/api/store/payment-collections/[id]/payment-sessions/route.ts +0 -1
  74. package/src/api/store/paypal/capture-order/route.ts +3 -4
  75. package/src/api/store/paypal/config/route.ts +99 -102
  76. package/src/api/store/paypal/create-order/route.ts +3 -13
  77. package/src/api/store/paypal-complete/route.ts +0 -10
  78. package/src/modules/paypal/clients/paypal-seller.client.ts +62 -59
  79. package/src/modules/paypal/models/paypal_connection.ts +2 -2
  80. package/src/modules/paypal/payment-provider/card-service.ts +9 -4
  81. package/src/modules/paypal/payment-provider/index.ts +15 -19
  82. package/src/modules/paypal/payment-provider/service.ts +12 -24
  83. package/src/modules/paypal/service.ts +21 -5
  84. package/src/modules/paypal/types/config.ts +2 -2
  85. package/src/modules/paypal/utils/paypal-auth.ts +31 -32
  86. package/src/modules/paypal/webhook-processor.ts +0 -14
  87. package/src/providers/paypal/index.ts +8 -10
  88. package/src/providers/paypal_card/index.ts +8 -10
  89. package/src/subscribers/paypal-order-invoice.ts +0 -16
@@ -1,19 +1,15 @@
1
- import { ModuleProvider, Modules } from "@medusajs/framework/utils"
2
- import PayPalPaymentProvider from "./service"
3
- import PayPalAdvancedCardProvider from "./card-service"
4
-
5
- /**
6
- * Payment Module Provider definition
7
- * Medusa will scan `services` to register payment providers on boot.
8
- */
9
- export default ModuleProvider(Modules.PAYMENT, {
10
- services: [
11
- PayPalPaymentProvider,
12
- PayPalAdvancedCardProvider,
13
- ],
14
- })
15
-
16
- export {
17
- PayPalPaymentProvider,
18
- PayPalAdvancedCardProvider,
19
- }
1
+ import { ModuleProvider, Modules } from "@medusajs/framework/utils"
2
+ import PayPalPaymentProvider from "./service"
3
+ import PayPalAdvancedCardProvider from "./card-service"
4
+
5
+ export default ModuleProvider(Modules.PAYMENT, {
6
+ services: [
7
+ PayPalPaymentProvider,
8
+ PayPalAdvancedCardProvider,
9
+ ],
10
+ })
11
+
12
+ export {
13
+ PayPalPaymentProvider,
14
+ PayPalAdvancedCardProvider,
15
+ }
@@ -34,6 +34,8 @@ import { getPayPalWebhookActionAndData } from "./webhook-utils"
34
34
 
35
35
  type Options = {}
36
36
 
37
+ const BN_CODE = "MBJTechnolabs_SI_SPB"
38
+
37
39
  function generateSessionId() {
38
40
  try {
39
41
  return randomUUID()
@@ -128,6 +130,7 @@ class PayPalPaymentProvider extends AbstractPaymentProvider<Options> {
128
130
  headers: {
129
131
  Authorization: `Basic ${_fbAuth}`,
130
132
  "Content-Type": "application/x-www-form-urlencoded",
133
+ "PayPal-Partner-Attribution-Id": BN_CODE,
131
134
  },
132
135
  body: "grant_type=client_credentials",
133
136
  })
@@ -148,6 +151,7 @@ class PayPalPaymentProvider extends AbstractPaymentProvider<Options> {
148
151
  headers: {
149
152
  Authorization: `Basic ${auth}`,
150
153
  "Content-Type": "application/x-www-form-urlencoded",
154
+ "PayPal-Partner-Attribution-Id": BN_CODE,
151
155
  },
152
156
  body: "grant_type=client_credentials",
153
157
  })
@@ -168,6 +172,7 @@ class PayPalPaymentProvider extends AbstractPaymentProvider<Options> {
168
172
  headers: {
169
173
  Authorization: `Bearer ${accessToken}`,
170
174
  "Content-Type": "application/json",
175
+ "PayPal-Partner-Attribution-Id": BN_CODE,
171
176
  },
172
177
  })
173
178
 
@@ -259,7 +264,6 @@ class PayPalPaymentProvider extends AbstractPaymentProvider<Options> {
259
264
  await paypal.recordAuditEvent(eventType, metadata)
260
265
  await paypal.recordMetric(eventType)
261
266
  } catch {
262
- // ignore
263
267
  }
264
268
  }
265
269
 
@@ -269,7 +273,6 @@ class PayPalPaymentProvider extends AbstractPaymentProvider<Options> {
269
273
  try {
270
274
  await paypal.recordMetric(metricName)
271
275
  } catch {
272
- // ignore
273
276
  }
274
277
  }
275
278
 
@@ -279,7 +282,6 @@ class PayPalPaymentProvider extends AbstractPaymentProvider<Options> {
279
282
  try {
280
283
  await paypal.recordPaymentLog(eventType, metadata)
281
284
  } catch {
282
- // ignore
283
285
  }
284
286
  }
285
287
 
@@ -363,7 +365,6 @@ class PayPalPaymentProvider extends AbstractPaymentProvider<Options> {
363
365
  const returnStatus = paymentActionRaw === "authorize" ? "authorized" : "captured"
364
366
  const timestampKey = paymentActionRaw === "authorize" ? "authorized_at" : "captured_at"
365
367
 
366
- // ── CASE 1: Already processed (has capture_id, authorization_id, or timestamp) ──
367
368
  if (
368
369
  existingPayPal.capture_id ||
369
370
  existingPayPal.authorization_id ||
@@ -380,11 +381,6 @@ class PayPalPaymentProvider extends AbstractPaymentProvider<Options> {
380
381
  }
381
382
  }
382
383
 
383
- // ── CASE 2: Has order_id — fetch current status from PayPal ──────────────
384
- // This is the KEY fix: capture-order already processed the payment but
385
- // Medusa's cart/complete workflow calls authorizePayment again with the
386
- // original session data (before capture-order wrote authorization_id).
387
- // We fetch the live PayPal order to get the real status.
388
384
  const orderId = String(existingPayPal.order_id || data.order_id || "")
389
385
  if (orderId) {
390
386
  try {
@@ -393,7 +389,6 @@ class PayPalPaymentProvider extends AbstractPaymentProvider<Options> {
393
389
  const capture = order?.purchase_units?.[0]?.payments?.captures?.[0]
394
390
  const authorization = order?.purchase_units?.[0]?.payments?.authorizations?.[0]
395
391
 
396
- // Order was already captured or authorized by capture-order route
397
392
  if (capture?.id || authorization?.id) {
398
393
  console.info("[PayPal] authorizePayment: order already processed by PayPal, returning", returnStatus)
399
394
  return {
@@ -412,8 +407,6 @@ class PayPalPaymentProvider extends AbstractPaymentProvider<Options> {
412
407
  }
413
408
  }
414
409
 
415
- // Order exists and is APPROVED — customer completed PayPal flow
416
- // but capture-order hasn't run yet (edge case). Mark as authorized.
417
410
  if (["APPROVED", "CREATED", "SAVED"].includes(String(order?.status || "").toUpperCase())) {
418
411
  console.info("[PayPal] authorizePayment: order approved, marking authorized")
419
412
  return {
@@ -430,14 +423,10 @@ class PayPalPaymentProvider extends AbstractPaymentProvider<Options> {
430
423
  }
431
424
  }
432
425
  } catch (e: any) {
433
- // Non-fatal — log and fall through to creating a new order below
434
426
  console.warn("[PayPal] authorizePayment: order lookup failed:", e?.message)
435
427
  }
436
428
  }
437
429
 
438
- // ── CASE 3: No order_id — create a new PayPal order ──────────────────────
439
- // This handles the rare case where Medusa calls authorizePayment before
440
- // the frontend has created a PayPal order (e.g. admin-created orders).
441
430
  const requestId = this.getIdempotencyKey(input, "authorize")
442
431
  let debugId: string | null = null
443
432
  const orderIntent = paymentActionRaw === "authorize" ? "AUTHORIZE" : "CAPTURE"
@@ -469,6 +458,7 @@ class PayPalPaymentProvider extends AbstractPaymentProvider<Options> {
469
458
  Authorization: `Bearer ${accessToken}`,
470
459
  "Content-Type": "application/json",
471
460
  "PayPal-Request-Id": requestId,
461
+ "PayPal-Partner-Attribution-Id": BN_CODE,
472
462
  },
473
463
  body: JSON.stringify(orderPayload),
474
464
  })
@@ -505,6 +495,7 @@ class PayPalPaymentProvider extends AbstractPaymentProvider<Options> {
505
495
  Authorization: `Bearer ${accessToken}`,
506
496
  "Content-Type": "application/json",
507
497
  "PayPal-Request-Id": `${requestId}-auth`,
498
+ "PayPal-Partner-Attribution-Id": BN_CODE,
508
499
  },
509
500
  }
510
501
  )
@@ -687,6 +678,7 @@ class PayPalPaymentProvider extends AbstractPaymentProvider<Options> {
687
678
  Authorization: `Bearer ${accessToken}`,
688
679
  "Content-Type": "application/json",
689
680
  "PayPal-Request-Id": `${requestId}-auth`,
681
+ "PayPal-Partner-Attribution-Id": BN_CODE,
690
682
  },
691
683
  }
692
684
  )
@@ -737,6 +729,7 @@ class PayPalPaymentProvider extends AbstractPaymentProvider<Options> {
737
729
  Authorization: `Bearer ${accessToken}`,
738
730
  "Content-Type": "application/json",
739
731
  "PayPal-Request-Id": requestId,
732
+ "PayPal-Partner-Attribution-Id": BN_CODE,
740
733
  },
741
734
  body: JSON.stringify(capturePayload),
742
735
  })
@@ -818,19 +811,11 @@ class PayPalPaymentProvider extends AbstractPaymentProvider<Options> {
818
811
 
819
812
  const requestId = this.getIdempotencyKey(input, `refund-${captureId}`)
820
813
 
821
- // ── IMPORTANT: Use input.amount (major units e.g. 20.00) NOT data.amount ──
822
- // Medusa v2 passes the refund amount via input.amount in major currency units.
823
- // data.amount is the original session amount in minor units (e.g. 2000 cents).
824
- // formatAmountForPayPal divides by 100, so using data.amount would give 0.20
825
- // instead of 20.00. We must use input.amount directly and format it as-is.
826
814
  const currencyOverride = await this.resolveCurrencyOverride()
827
815
  const currencyCode = normalizeCurrencyCode(
828
816
  data.currency_code || currencyOverride || "EUR"
829
817
  )
830
818
 
831
- // input.amount is already in major units (e.g. 20.00 EUR)
832
- // Just convert to string with correct decimal places
833
- // getCurrencyExponent is already available via the amounts utils
834
819
  const exponent = currencyCode.toUpperCase() === "JPY" ? 0
835
820
  : ["BHD", "JOD", "KWD", "OMR", "TND"].includes(currencyCode.toUpperCase()) ? 3
836
821
  : 2
@@ -861,6 +846,7 @@ class PayPalPaymentProvider extends AbstractPaymentProvider<Options> {
861
846
  Authorization: `Bearer ${accessToken}`,
862
847
  "Content-Type": "application/json",
863
848
  "PayPal-Request-Id": requestId,
849
+ "PayPal-Partner-Attribution-Id": BN_CODE,
864
850
  },
865
851
  body: JSON.stringify(refundPayload),
866
852
  })
@@ -952,6 +938,7 @@ class PayPalPaymentProvider extends AbstractPaymentProvider<Options> {
952
938
  Authorization: `Bearer ${accessToken}`,
953
939
  "Content-Type": "application/json",
954
940
  "PayPal-Request-Id": requestId,
941
+ "PayPal-Partner-Attribution-Id": BN_CODE,
955
942
  },
956
943
  }
957
944
  )
@@ -981,6 +968,7 @@ class PayPalPaymentProvider extends AbstractPaymentProvider<Options> {
981
968
  Authorization: `Bearer ${accessToken}`,
982
969
  "Content-Type": "application/json",
983
970
  "PayPal-Request-Id": requestId,
971
+ "PayPal-Partner-Attribution-Id": BN_CODE,
984
972
  },
985
973
  body: JSON.stringify({}),
986
974
  })
@@ -23,6 +23,10 @@ class PayPalModuleService extends MedusaService({
23
23
  }) {
24
24
  protected cfg = getPayPalConfig()
25
25
 
26
+ private get bnCode(): string {
27
+ return this.cfg.bnCode || "MBJTechnolabs_SI_SPB"
28
+ }
29
+
26
30
  private async getSettingsData() {
27
31
  const settings = await this.getSettings()
28
32
  return (settings?.data || {}) as Record<string, any>
@@ -198,7 +202,6 @@ class PayPalModuleService extends MedusaService({
198
202
  throw new Error("Missing PayPal partner merchant id configuration.")
199
203
  }
200
204
 
201
- const { onboarding } = await this.ensureSettingsDefaults()
202
205
  const baseUrl = env === "live" ? "https://api-m.paypal.com" : "https://api-m.sandbox.paypal.com"
203
206
  const accessToken = accessTokenOverride ?? await this.getAppAccessToken()
204
207
 
@@ -211,7 +214,7 @@ class PayPalModuleService extends MedusaService({
211
214
  headers: {
212
215
  "Content-Type": "application/json",
213
216
  Authorization: `Bearer ${accessToken}`,
214
- ...(onboarding.bn_code ? { "PayPal-Partner-Attribution-Id": onboarding.bn_code } : {}),
217
+ "PayPal-Partner-Attribution-Id": this.bnCode,
215
218
  },
216
219
  }
217
220
  )
@@ -248,6 +251,7 @@ class PayPalModuleService extends MedusaService({
248
251
  headers: {
249
252
  "Content-Type": "application/x-www-form-urlencoded",
250
253
  Authorization: `Basic ${basic}`,
254
+ "PayPal-Partner-Attribution-Id": this.bnCode,
251
255
  },
252
256
  body,
253
257
  })
@@ -304,6 +308,7 @@ class PayPalModuleService extends MedusaService({
304
308
  Authorization: `Bearer ${accessToken}`,
305
309
  "Content-Type": "application/json",
306
310
  Accept: "application/json",
311
+ "PayPal-Partner-Attribution-Id": this.bnCode,
307
312
  },
308
313
  })
309
314
  if (userInfoResp.ok) {
@@ -329,6 +334,7 @@ class PayPalModuleService extends MedusaService({
329
334
  headers: {
330
335
  Authorization: `Bearer ${accessToken}`,
331
336
  "Content-Type": "application/json",
337
+ "PayPal-Partner-Attribution-Id": this.bnCode,
332
338
  },
333
339
  }
334
340
  )
@@ -467,6 +473,7 @@ class PayPalModuleService extends MedusaService({
467
473
  form.set("return_url", return_url)
468
474
  form.set("return_url_description", "Return to your shop.")
469
475
  form.set("partner_merchant_id", partner_merchant_id)
476
+ form.set("from", "medusa")
470
477
 
471
478
  const products = input?.products?.length ? input.products : ["PPCP"]
472
479
 
@@ -621,6 +628,7 @@ class PayPalModuleService extends MedusaService({
621
628
  headers: {
622
629
  "Content-Type": "application/x-www-form-urlencoded",
623
630
  Authorization: `Basic ${basic}`,
631
+ "PayPal-Partner-Attribution-Id": this.bnCode,
624
632
  },
625
633
  body: tokenBody,
626
634
  })
@@ -656,7 +664,7 @@ class PayPalModuleService extends MedusaService({
656
664
  headers: {
657
665
  "Content-Type": "application/json",
658
666
  Authorization: `Bearer ${sellerAccessToken}`,
659
- ...(onboarding.bn_code ? { "PayPal-Partner-Attribution-Id": onboarding.bn_code } : {}),
667
+ "PayPal-Partner-Attribution-Id": this.bnCode,
660
668
  },
661
669
  }
662
670
  )
@@ -900,6 +908,7 @@ class PayPalModuleService extends MedusaService({
900
908
  headers: {
901
909
  Authorization: `Bearer ${accessToken}`,
902
910
  "Content-Type": "application/json",
911
+ "PayPal-Partner-Attribution-Id": this.bnCode,
903
912
  },
904
913
  })
905
914
 
@@ -920,6 +929,7 @@ class PayPalModuleService extends MedusaService({
920
929
  headers: {
921
930
  Authorization: `Bearer ${accessToken}`,
922
931
  "Content-Type": "application/json",
932
+ "PayPal-Partner-Attribution-Id": this.bnCode,
923
933
  },
924
934
  body: JSON.stringify({
925
935
  url: webhookUrl,
@@ -1093,7 +1103,11 @@ class PayPalModuleService extends MedusaService({
1093
1103
 
1094
1104
  const res = await fetch(`${baseUrl}/v1/oauth2/token`, {
1095
1105
  method: "POST",
1096
- headers: { "Content-Type": "application/x-www-form-urlencoded", Authorization: `Basic ${basic}` },
1106
+ headers: {
1107
+ "Content-Type": "application/x-www-form-urlencoded",
1108
+ Authorization: `Basic ${basic}`,
1109
+ "PayPal-Partner-Attribution-Id": this.bnCode,
1110
+ },
1097
1111
  body,
1098
1112
  })
1099
1113
 
@@ -1125,8 +1139,8 @@ class PayPalModuleService extends MedusaService({
1125
1139
  Authorization: `Bearer ${accessToken}`,
1126
1140
  "Content-Type": "application/json",
1127
1141
  Accept: "application/json",
1142
+ "PayPal-Partner-Attribution-Id": this.bnCode,
1128
1143
  ...(opts?.locale ? { "Accept-Language": opts.locale } : {}),
1129
- ...(this.cfg.bnCode ? { "PayPal-Partner-Attribution-Id": this.cfg.bnCode } : {}),
1130
1144
  },
1131
1145
  })
1132
1146
 
@@ -1230,6 +1244,7 @@ class PayPalModuleService extends MedusaService({
1230
1244
  headers: {
1231
1245
  Authorization: `Basic ${auth}`,
1232
1246
  "Content-Type": "application/x-www-form-urlencoded",
1247
+ "PayPal-Partner-Attribution-Id": this.bnCode,
1233
1248
  },
1234
1249
  body: "grant_type=client_credentials",
1235
1250
  })
@@ -1247,6 +1262,7 @@ class PayPalModuleService extends MedusaService({
1247
1262
  headers: {
1248
1263
  Authorization: `Bearer ${accessToken}`,
1249
1264
  "Content-Type": "application/json",
1265
+ "PayPal-Partner-Attribution-Id": this.bnCode,
1250
1266
  },
1251
1267
  })
1252
1268
 
@@ -10,11 +10,11 @@ export type PayPalModuleConfig = {
10
10
  }
11
11
 
12
12
  const STATIC_CFG: PayPalModuleConfig = {
13
- partnerServiceUrl: "https://mbjtechnolabs.com/ppcp-seller-onboarding/seller-onboarding.php",
13
+ partnerServiceUrl: "https://mbjtechnolabs.com/ppcp-seller-onboarding/seller-onboarding.php?from=medusa",
14
14
  partnerJsUrl: "https://www.paypal.com/webapps/merchantboarding/js/lib/lightbox/partner.js",
15
15
  backendUrl: "http://localhost:9000",
16
16
  sellerNonce: "a1233wtergfsdt4365tzrshgfbaewa36AGa1233wtergfsdt4365tzrshgfbaewa36AG",
17
- bnCode: "",
17
+ bnCode: "MBJTechnolabs_SI_SPB",
18
18
  partnerMerchantIdSandbox: "K6QLN2LPGQRHL",
19
19
  partnerMerchantIdLive: "GT5R877JNBPLL",
20
20
  alertWebhookUrls: [],
@@ -1,32 +1,31 @@
1
- /**
2
- * Shared PayPal API authentication helpers.
3
- * Import these instead of copying into each route.
4
- */
5
- export function getPayPalApiBase(environment: string): string {
6
- return environment === "live"
7
- ? "https://api-m.paypal.com"
8
- : "https://api-m.sandbox.paypal.com"
9
- }
10
-
11
- export async function getPayPalAccessToken(opts: {
12
- environment: string
13
- client_id: string
14
- client_secret: string
15
- }): Promise<{ accessToken: string; base: string }> {
16
- const base = getPayPalApiBase(opts.environment)
17
- const auth = Buffer.from(`${opts.client_id}:${opts.client_secret}`).toString("base64")
18
- const resp = await fetch(`${base}/v1/oauth2/token`, {
19
- method: "POST",
20
- headers: {
21
- Authorization: `Basic ${auth}`,
22
- "Content-Type": "application/x-www-form-urlencoded",
23
- },
24
- body: "grant_type=client_credentials",
25
- })
26
- const text = await resp.text()
27
- if (!resp.ok) {
28
- throw new Error(`PayPal token error (${resp.status}): ${text}`)
29
- }
30
- const json = JSON.parse(text)
31
- return { accessToken: String(json.access_token), base }
32
- }
1
+ export function getPayPalApiBase(environment: string): string {
2
+ return environment === "live"
3
+ ? "https://api-m.paypal.com"
4
+ : "https://api-m.sandbox.paypal.com"
5
+ }
6
+
7
+ const BN_CODE = "MBJTechnolabs_SI_SPB"
8
+
9
+ export async function getPayPalAccessToken(opts: {
10
+ environment: string
11
+ client_id: string
12
+ client_secret: string
13
+ }): Promise<{ accessToken: string; base: string }> {
14
+ const base = getPayPalApiBase(opts.environment)
15
+ const auth = Buffer.from(`${opts.client_id}:${opts.client_secret}`).toString("base64")
16
+ const resp = await fetch(`${base}/v1/oauth2/token`, {
17
+ method: "POST",
18
+ headers: {
19
+ Authorization: `Basic ${auth}`,
20
+ "Content-Type": "application/x-www-form-urlencoded",
21
+ "PayPal-Partner-Attribution-Id": BN_CODE,
22
+ },
23
+ body: "grant_type=client_credentials",
24
+ })
25
+ const text = await resp.text()
26
+ if (!resp.ok) {
27
+ throw new Error(`PayPal token error (${resp.status}): ${text}`)
28
+ }
29
+ const json = JSON.parse(text)
30
+ return { accessToken: String(json.access_token), base }
31
+ }
@@ -2,7 +2,6 @@ import type { MedusaContainer } from "@medusajs/framework/types"
2
2
  import { Modules } from "@medusajs/framework/utils"
3
3
  import { isPayPalProviderId } from "./utils/provider-ids"
4
4
 
5
- // ─── Event → Medusa status mapping ───────────────────────────────────────────
6
5
 
7
6
  export const EVENT_STATUS_MAP: Record<
8
7
  string,
@@ -23,9 +22,6 @@ export const EVENT_STATUS_MAP: Record<
23
22
  "PAYMENT.REFUND.DENIED": "error",
24
23
  }
25
24
 
26
- // ─── Status transition guard ──────────────────────────────────────────────────
27
- // Only allow forward/meaningful moves.
28
- // Prevents a late-arriving webhook from downgrading an already-captured payment.
29
25
 
30
26
  const ALLOWED_TRANSITIONS: Record<string, Set<string>> = {
31
27
  pending: new Set(["authorized", "captured", "canceled", "error"]),
@@ -39,7 +35,6 @@ export function isTransitionAllowed(from: string, to: string): boolean {
39
35
  return ALLOWED_TRANSITIONS[from]?.has(to) ?? false
40
36
  }
41
37
 
42
- // ─── Event type helpers ───────────────────────────────────────────────────────
43
38
 
44
39
  export const SUPPORTED_EVENT_PREFIXES = [
45
40
  "PAYMENT.CAPTURE.",
@@ -52,9 +47,6 @@ export function isAllowedEventType(eventType: string): boolean {
52
47
  return SUPPORTED_EVENT_PREFIXES.some((prefix) => eventType.startsWith(prefix))
53
48
  }
54
49
 
55
- // ─── Error classification ─────────────────────────────────────────────────────
56
- // Non-retryable: event is permanently unprocessable (wrong cart, missing session).
57
- // Retryable: transient failure (DB down, network error) — worth trying again.
58
50
 
59
51
  const NON_RETRYABLE_PATTERNS = [
60
52
  "payment collection not found",
@@ -71,7 +63,6 @@ export function isRetryableError(error: unknown): boolean {
71
63
  return !NON_RETRYABLE_PATTERNS.some((p) => message.includes(p))
72
64
  }
73
65
 
74
- // ─── Retry schedule ───────────────────────────────────────────────────────────
75
66
 
76
67
  const RETRY_SCHEDULE_MINUTES = [2, 10, 30, 60, 120]
77
68
  export const MAX_WEBHOOK_ATTEMPTS = RETRY_SCHEDULE_MINUTES.length + 1
@@ -83,7 +74,6 @@ export function computeNextRetryAt(attemptCount: number): Date | null {
83
74
  return new Date(Date.now() + delayMinutes * 60 * 1000)
84
75
  }
85
76
 
86
- // ─── Payload normalisation ────────────────────────────────────────────────────
87
77
 
88
78
  export function normalizeResource(payload: Record<string, any>): Record<string, any> {
89
79
  const resource = payload?.resource
@@ -109,7 +99,6 @@ export function normalizeEventVersion(payload: Record<string, any>): string | nu
109
99
  return String(raw).trim().replace(/^v/i, "")
110
100
  }
111
101
 
112
- // ─── Identifier extraction ────────────────────────────────────────────────────
113
102
 
114
103
  export interface ExtractedIdentifiers {
115
104
  orderId: string | null
@@ -160,7 +149,6 @@ export function extractIdentifiers(
160
149
  return { orderId, captureId, refundId, cartId }
161
150
  }
162
151
 
163
- // ─── Session lookup ───────────────────────────────────────────────────────────
164
152
 
165
153
  interface ResolvedSession {
166
154
  sessionId: string
@@ -216,7 +204,6 @@ async function findPayPalSession(
216
204
  }
217
205
  }
218
206
 
219
- // ─── Session update ───────────────────────────────────────────────────────────
220
207
 
221
208
  function mergeRefunds(existing: any[], incoming: any[]): any[] {
222
209
  const seen = new Set<string>()
@@ -270,7 +257,6 @@ async function applyStatusToSession(
270
257
  })
271
258
  }
272
259
 
273
- // ─── Main event processor ─────────────────────────────────────────────────────
274
260
 
275
261
  export interface ProcessResult {
276
262
  orderId: string | null
@@ -1,10 +1,8 @@
1
- import { ModuleProvider, Modules } from "@medusajs/framework/utils"
2
- import { PayPalPaymentProvider } from "../../modules/paypal/payment-provider/service"
3
-
4
- // Medusa Payment Module Provider entrypoint (doc-aligned)
5
- export default ModuleProvider(Modules.PAYMENT, {
6
- services: [PayPalPaymentProvider],
7
- })
8
-
9
- // Optional named export (doesn't hurt, can help for testing/imports)
10
- export { PayPalPaymentProvider }
1
+ import { ModuleProvider, Modules } from "@medusajs/framework/utils"
2
+ import { PayPalPaymentProvider } from "../../modules/paypal/payment-provider/service"
3
+
4
+ export default ModuleProvider(Modules.PAYMENT, {
5
+ services: [PayPalPaymentProvider],
6
+ })
7
+
8
+ export { PayPalPaymentProvider }
@@ -1,10 +1,8 @@
1
- import { ModuleProvider, Modules } from "@medusajs/framework/utils"
2
- import { PayPalAdvancedCardProvider } from "../../modules/paypal/payment-provider/card-service"
3
-
4
- // Medusa Payment Module Provider entrypoint (doc-aligned)
5
- export default ModuleProvider(Modules.PAYMENT, {
6
- services: [PayPalAdvancedCardProvider],
7
- })
8
-
9
- // Optional named export
10
- export { PayPalAdvancedCardProvider }
1
+ import { ModuleProvider, Modules } from "@medusajs/framework/utils"
2
+ import { PayPalAdvancedCardProvider } from "../../modules/paypal/payment-provider/card-service"
3
+
4
+ export default ModuleProvider(Modules.PAYMENT, {
5
+ services: [PayPalAdvancedCardProvider],
6
+ })
7
+
8
+ export { PayPalAdvancedCardProvider }
@@ -3,8 +3,6 @@ import type PayPalModuleService from "../modules/paypal/service"
3
3
  import { getPayPalAccessToken } from "../modules/paypal/utils/paypal-auth"
4
4
  import { isPayPalProviderId } from "../modules/paypal/utils/provider-ids"
5
5
 
6
- // PayPal orders can only be PATCHed in CREATED or APPROVED status.
7
- // Once COMPLETED (post-capture) the order is immutable.
8
6
  const PATCHABLE_STATUSES = new Set(["CREATED", "APPROVED", "SAVED"])
9
7
 
10
8
  export default async function paypalOrderInvoiceHandler({
@@ -18,7 +16,6 @@ export default async function paypalOrderInvoiceHandler({
18
16
  const query = container.resolve("query") as any
19
17
  const paypal = container.resolve<PayPalModuleService>("paypal_onboarding")
20
18
 
21
- // Fetch the Medusa order with payment session data
22
19
  const { data: orders } = await query.graph({
23
20
  entity: "order",
24
21
  fields: [
@@ -36,7 +33,6 @@ export default async function paypalOrderInvoiceHandler({
36
33
  const order = orders?.[0]
37
34
  if (!order) return
38
35
 
39
- // Find the PayPal payment session
40
36
  const sessions = (order.payment_collections || []).flatMap(
41
37
  (pc: any) => pc.payment_sessions || []
42
38
  )
@@ -69,7 +65,6 @@ export default async function paypalOrderInvoiceHandler({
69
65
  return
70
66
  }
71
67
 
72
- // Build the target invoice_id using display_id (ideal format)
73
68
  const settings = await paypal.getSettings().catch(() => ({}))
74
69
  const settingsData =
75
70
  settings && typeof settings === "object" && "data" in settings
@@ -87,14 +82,9 @@ export default async function paypalOrderInvoiceHandler({
87
82
 
88
83
  if (!invoiceId) return
89
84
 
90
- // Get PayPal access token
91
85
  const creds = await paypal.getActiveCredentials()
92
86
  const { accessToken, base } = await getPayPalAccessToken(creds)
93
87
 
94
- // Check PayPal order status before attempting PATCH.
95
- // PayPal only allows PATCH on CREATED or APPROVED orders.
96
- // COMPLETED orders (post-capture) are immutable, so skip PATCH and
97
- // log the reconciliation mapping instead.
98
88
  let paypalOrderStatus = ""
99
89
  let currentInvoiceId = ""
100
90
  try {
@@ -114,7 +104,6 @@ export default async function paypalOrderInvoiceHandler({
114
104
  )
115
105
  }
116
106
 
117
- // Always log the display_id -> paypalOrderId mapping for reconciliation
118
107
  console.info("[PayPal] invoice reconciliation mapping:", {
119
108
  medusaOrderId: orderId,
120
109
  displayId,
@@ -124,7 +113,6 @@ export default async function paypalOrderInvoiceHandler({
124
113
  currentInvoiceId,
125
114
  })
126
115
 
127
- // If order is COMPLETED, invoice_id is immutable — skip PATCH
128
116
  if (paypalOrderStatus && !PATCHABLE_STATUSES.has(paypalOrderStatus)) {
129
117
  console.info(
130
118
  `[PayPal] invoice_id PATCH skipped — order status is ${paypalOrderStatus} (immutable).`,
@@ -133,7 +121,6 @@ export default async function paypalOrderInvoiceHandler({
133
121
  return
134
122
  }
135
123
 
136
- // If invoice_id already matches target, skip PATCH
137
124
  if (currentInvoiceId === invoiceId) {
138
125
  console.info(
139
126
  `[PayPal] invoice_id already "${invoiceId}" — skipping PATCH`
@@ -141,8 +128,6 @@ export default async function paypalOrderInvoiceHandler({
141
128
  return
142
129
  }
143
130
 
144
- // Attempt PATCH only for CREATED/APPROVED orders.
145
- // This handles the authorize-only flow where capture hasn't happened yet.
146
131
  const patchResp = await fetch(
147
132
  `${base}/v2/checkout/orders/${paypalOrderId}`,
148
133
  {
@@ -177,7 +162,6 @@ export default async function paypalOrderInvoiceHandler({
177
162
  })
178
163
  }
179
164
  } catch (e: any) {
180
- // Non-fatal — never block order placement
181
165
  console.warn("[PayPal] paypalOrderInvoiceHandler error:", e?.message || e)
182
166
  }
183
167
  }