@easypayment/medusa-payment-paypal 0.7.4
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.
- package/.medusa/server/src/admin/index.js +1267 -0
- package/.medusa/server/src/admin/index.mjs +1268 -0
- package/.medusa/server/src/api/admin/payment-collections/[id]/payment-sessions/route.d.ts +3 -0
- package/.medusa/server/src/api/admin/payment-collections/[id]/payment-sessions/route.d.ts.map +1 -0
- package/.medusa/server/src/api/admin/payment-collections/[id]/payment-sessions/route.js +18 -0
- package/.medusa/server/src/api/admin/payment-collections/[id]/payment-sessions/route.js.map +1 -0
- package/.medusa/server/src/api/admin/paypal/disconnect/route.d.ts +3 -0
- package/.medusa/server/src/api/admin/paypal/disconnect/route.d.ts.map +1 -0
- package/.medusa/server/src/api/admin/paypal/disconnect/route.js +9 -0
- package/.medusa/server/src/api/admin/paypal/disconnect/route.js.map +1 -0
- package/.medusa/server/src/api/admin/paypal/environment/route.d.ts +4 -0
- package/.medusa/server/src/api/admin/paypal/environment/route.d.ts.map +1 -0
- package/.medusa/server/src/api/admin/paypal/environment/route.js +23 -0
- package/.medusa/server/src/api/admin/paypal/environment/route.js.map +1 -0
- package/.medusa/server/src/api/admin/paypal/onboard-complete/route.d.ts +4 -0
- package/.medusa/server/src/api/admin/paypal/onboard-complete/route.d.ts.map +1 -0
- package/.medusa/server/src/api/admin/paypal/onboard-complete/route.js +31 -0
- package/.medusa/server/src/api/admin/paypal/onboard-complete/route.js.map +1 -0
- package/.medusa/server/src/api/admin/paypal/onboarding-link/route.d.ts +4 -0
- package/.medusa/server/src/api/admin/paypal/onboarding-link/route.d.ts.map +1 -0
- package/.medusa/server/src/api/admin/paypal/onboarding-link/route.js +35 -0
- package/.medusa/server/src/api/admin/paypal/onboarding-link/route.js.map +1 -0
- package/.medusa/server/src/api/admin/paypal/onboarding-status/route.d.ts +3 -0
- package/.medusa/server/src/api/admin/paypal/onboarding-status/route.d.ts.map +1 -0
- package/.medusa/server/src/api/admin/paypal/onboarding-status/route.js +19 -0
- package/.medusa/server/src/api/admin/paypal/onboarding-status/route.js.map +1 -0
- package/.medusa/server/src/api/admin/paypal/save-credentials/route.d.ts +3 -0
- package/.medusa/server/src/api/admin/paypal/save-credentials/route.d.ts.map +1 -0
- package/.medusa/server/src/api/admin/paypal/save-credentials/route.js +17 -0
- package/.medusa/server/src/api/admin/paypal/save-credentials/route.js.map +1 -0
- package/.medusa/server/src/api/admin/paypal/settings/route.d.ts +4 -0
- package/.medusa/server/src/api/admin/paypal/settings/route.d.ts.map +1 -0
- package/.medusa/server/src/api/admin/paypal/settings/route.js +14 -0
- package/.medusa/server/src/api/admin/paypal/settings/route.js.map +1 -0
- package/.medusa/server/src/api/admin/paypal/status/route.d.ts +3 -0
- package/.medusa/server/src/api/admin/paypal/status/route.d.ts.map +1 -0
- package/.medusa/server/src/api/admin/paypal/status/route.js +11 -0
- package/.medusa/server/src/api/admin/paypal/status/route.js.map +1 -0
- package/.medusa/server/src/api/middlewares.d.ts +3 -0
- package/.medusa/server/src/api/middlewares.d.ts.map +1 -0
- package/.medusa/server/src/api/middlewares.js +16 -0
- package/.medusa/server/src/api/middlewares.js.map +1 -0
- package/.medusa/server/src/api/store/payment-collections/[id]/payment-sessions/route.d.ts +3 -0
- package/.medusa/server/src/api/store/payment-collections/[id]/payment-sessions/route.d.ts.map +1 -0
- package/.medusa/server/src/api/store/payment-collections/[id]/payment-sessions/route.js +43 -0
- package/.medusa/server/src/api/store/payment-collections/[id]/payment-sessions/route.js.map +1 -0
- package/.medusa/server/src/api/store/paypal/capture-order/route.d.ts +3 -0
- package/.medusa/server/src/api/store/paypal/capture-order/route.d.ts.map +1 -0
- package/.medusa/server/src/api/store/paypal/capture-order/route.js +204 -0
- package/.medusa/server/src/api/store/paypal/capture-order/route.js.map +1 -0
- package/.medusa/server/src/api/store/paypal/config/route.d.ts +3 -0
- package/.medusa/server/src/api/store/paypal/config/route.d.ts.map +1 -0
- package/.medusa/server/src/api/store/paypal/config/route.js +77 -0
- package/.medusa/server/src/api/store/paypal/config/route.js.map +1 -0
- package/.medusa/server/src/api/store/paypal/create-order/route.d.ts +3 -0
- package/.medusa/server/src/api/store/paypal/create-order/route.d.ts.map +1 -0
- package/.medusa/server/src/api/store/paypal/create-order/route.js +361 -0
- package/.medusa/server/src/api/store/paypal/create-order/route.js.map +1 -0
- package/.medusa/server/src/api/store/paypal/settings/route.d.ts +3 -0
- package/.medusa/server/src/api/store/paypal/settings/route.d.ts.map +1 -0
- package/.medusa/server/src/api/store/paypal/settings/route.js +20 -0
- package/.medusa/server/src/api/store/paypal/settings/route.js.map +1 -0
- package/.medusa/server/src/api/store/paypal/webhook/route.d.ts +3 -0
- package/.medusa/server/src/api/store/paypal/webhook/route.d.ts.map +1 -0
- package/.medusa/server/src/api/store/paypal/webhook/route.js +250 -0
- package/.medusa/server/src/api/store/paypal/webhook/route.js.map +1 -0
- package/.medusa/server/src/api/store/paypal-complete/route.d.ts +3 -0
- package/.medusa/server/src/api/store/paypal-complete/route.d.ts.map +1 -0
- package/.medusa/server/src/api/store/paypal-complete/route.js +76 -0
- package/.medusa/server/src/api/store/paypal-complete/route.js.map +1 -0
- package/.medusa/server/src/index.d.ts +2 -0
- package/.medusa/server/src/index.d.ts.map +1 -0
- package/.medusa/server/src/index.js +9 -0
- package/.medusa/server/src/index.js.map +1 -0
- package/.medusa/server/src/jobs/paypal-webhook-retry.d.ts +7 -0
- package/.medusa/server/src/jobs/paypal-webhook-retry.d.ts.map +1 -0
- package/.medusa/server/src/jobs/paypal-webhook-retry.js +132 -0
- package/.medusa/server/src/jobs/paypal-webhook-retry.js.map +1 -0
- package/.medusa/server/src/modules/paypal/clients/paypal-seller.client.d.ts +15 -0
- package/.medusa/server/src/modules/paypal/clients/paypal-seller.client.d.ts.map +1 -0
- package/.medusa/server/src/modules/paypal/clients/paypal-seller.client.js +67 -0
- package/.medusa/server/src/modules/paypal/clients/paypal-seller.client.js.map +1 -0
- package/.medusa/server/src/modules/paypal/index.d.ts +64 -0
- package/.medusa/server/src/modules/paypal/index.d.ts.map +1 -0
- package/.medusa/server/src/modules/paypal/index.js +13 -0
- package/.medusa/server/src/modules/paypal/index.js.map +1 -0
- package/.medusa/server/src/modules/paypal/migrations/20260115120000_create_paypal_connection.d.ts +6 -0
- package/.medusa/server/src/modules/paypal/migrations/20260115120000_create_paypal_connection.d.ts.map +1 -0
- package/.medusa/server/src/modules/paypal/migrations/20260115120000_create_paypal_connection.js +36 -0
- package/.medusa/server/src/modules/paypal/migrations/20260115120000_create_paypal_connection.js.map +1 -0
- package/.medusa/server/src/modules/paypal/migrations/20260123090000_create_paypal_settings.d.ts +6 -0
- package/.medusa/server/src/modules/paypal/migrations/20260123090000_create_paypal_settings.d.ts.map +1 -0
- package/.medusa/server/src/modules/paypal/migrations/20260123090000_create_paypal_settings.js +25 -0
- package/.medusa/server/src/modules/paypal/migrations/20260123090000_create_paypal_settings.js.map +1 -0
- package/.medusa/server/src/modules/paypal/migrations/20260201090000_create_paypal_webhook_event.d.ts +6 -0
- package/.medusa/server/src/modules/paypal/migrations/20260201090000_create_paypal_webhook_event.d.ts.map +1 -0
- package/.medusa/server/src/modules/paypal/migrations/20260201090000_create_paypal_webhook_event.js +32 -0
- package/.medusa/server/src/modules/paypal/migrations/20260201090000_create_paypal_webhook_event.js.map +1 -0
- package/.medusa/server/src/modules/paypal/migrations/20260401090000_create_paypal_metric.d.ts +6 -0
- package/.medusa/server/src/modules/paypal/migrations/20260401090000_create_paypal_metric.d.ts.map +1 -0
- package/.medusa/server/src/modules/paypal/migrations/20260401090000_create_paypal_metric.js +30 -0
- package/.medusa/server/src/modules/paypal/migrations/20260401090000_create_paypal_metric.js.map +1 -0
- package/.medusa/server/src/modules/paypal/migrations/20260701090000_add_paypal_webhook_event_processing.d.ts +6 -0
- package/.medusa/server/src/modules/paypal/migrations/20260701090000_add_paypal_webhook_event_processing.d.ts.map +1 -0
- package/.medusa/server/src/modules/paypal/migrations/20260701090000_add_paypal_webhook_event_processing.js +34 -0
- package/.medusa/server/src/modules/paypal/migrations/20260701090000_add_paypal_webhook_event_processing.js.map +1 -0
- package/.medusa/server/src/modules/paypal/migrations/20261101090000_remove_paypal_reconciliation_status.d.ts +6 -0
- package/.medusa/server/src/modules/paypal/migrations/20261101090000_remove_paypal_reconciliation_status.d.ts.map +1 -0
- package/.medusa/server/src/modules/paypal/migrations/20261101090000_remove_paypal_reconciliation_status.js +28 -0
- package/.medusa/server/src/modules/paypal/migrations/20261101090000_remove_paypal_reconciliation_status.js.map +1 -0
- package/.medusa/server/src/modules/paypal/migrations/20261201090000_remove_paypal_audit_log.d.ts +6 -0
- package/.medusa/server/src/modules/paypal/migrations/20261201090000_remove_paypal_audit_log.d.ts.map +1 -0
- package/.medusa/server/src/modules/paypal/migrations/20261201090000_remove_paypal_audit_log.js +29 -0
- package/.medusa/server/src/modules/paypal/migrations/20261201090000_remove_paypal_audit_log.js.map +1 -0
- package/.medusa/server/src/modules/paypal/migrations/20270101090000_set_paypal_environment_default_live.d.ts +6 -0
- package/.medusa/server/src/modules/paypal/migrations/20270101090000_set_paypal_environment_default_live.d.ts.map +1 -0
- package/.medusa/server/src/modules/paypal/migrations/20270101090000_set_paypal_environment_default_live.js +14 -0
- package/.medusa/server/src/modules/paypal/migrations/20270101090000_set_paypal_environment_default_live.js.map +1 -0
- package/.medusa/server/src/modules/paypal/migrations/20270102090000_add_paypal_seller_email.d.ts +6 -0
- package/.medusa/server/src/modules/paypal/migrations/20270102090000_add_paypal_seller_email.d.ts.map +1 -0
- package/.medusa/server/src/modules/paypal/migrations/20270102090000_add_paypal_seller_email.js +14 -0
- package/.medusa/server/src/modules/paypal/migrations/20270102090000_add_paypal_seller_email.js.map +1 -0
- package/.medusa/server/src/modules/paypal/migrations/20270103090000_add_paypal_seller_merchant_id.d.ts +6 -0
- package/.medusa/server/src/modules/paypal/migrations/20270103090000_add_paypal_seller_merchant_id.d.ts.map +1 -0
- package/.medusa/server/src/modules/paypal/migrations/20270103090000_add_paypal_seller_merchant_id.js +14 -0
- package/.medusa/server/src/modules/paypal/migrations/20270103090000_add_paypal_seller_merchant_id.js.map +1 -0
- package/.medusa/server/src/modules/paypal/migrations/20270201000000_add_webhook_dead_letter.d.ts +6 -0
- package/.medusa/server/src/modules/paypal/migrations/20270201000000_add_webhook_dead_letter.d.ts.map +1 -0
- package/.medusa/server/src/modules/paypal/migrations/20270201000000_add_webhook_dead_letter.js +20 -0
- package/.medusa/server/src/modules/paypal/migrations/20270201000000_add_webhook_dead_letter.js.map +1 -0
- package/.medusa/server/src/modules/paypal/models/paypal_connection.d.ts +16 -0
- package/.medusa/server/src/modules/paypal/models/paypal_connection.d.ts.map +1 -0
- package/.medusa/server/src/modules/paypal/models/paypal_connection.js +19 -0
- package/.medusa/server/src/modules/paypal/models/paypal_connection.js.map +1 -0
- package/.medusa/server/src/modules/paypal/models/paypal_metric.d.ts +7 -0
- package/.medusa/server/src/modules/paypal/models/paypal_metric.d.ts.map +1 -0
- package/.medusa/server/src/modules/paypal/models/paypal_metric.js +10 -0
- package/.medusa/server/src/modules/paypal/models/paypal_metric.js.map +1 -0
- package/.medusa/server/src/modules/paypal/models/paypal_settings.d.ts +6 -0
- package/.medusa/server/src/modules/paypal/models/paypal_settings.d.ts.map +1 -0
- package/.medusa/server/src/modules/paypal/models/paypal_settings.js +9 -0
- package/.medusa/server/src/modules/paypal/models/paypal_settings.js.map +1 -0
- package/.medusa/server/src/modules/paypal/models/paypal_webhook_event.d.ts +17 -0
- package/.medusa/server/src/modules/paypal/models/paypal_webhook_event.d.ts.map +1 -0
- package/.medusa/server/src/modules/paypal/models/paypal_webhook_event.js +20 -0
- package/.medusa/server/src/modules/paypal/models/paypal_webhook_event.js.map +1 -0
- package/.medusa/server/src/modules/paypal/payment-provider/card-service.d.ts +32 -0
- package/.medusa/server/src/modules/paypal/payment-provider/card-service.d.ts.map +1 -0
- package/.medusa/server/src/modules/paypal/payment-provider/card-service.js +625 -0
- package/.medusa/server/src/modules/paypal/payment-provider/card-service.js.map +1 -0
- package/.medusa/server/src/modules/paypal/payment-provider/index.d.ts +6 -0
- package/.medusa/server/src/modules/paypal/payment-provider/index.d.ts.map +1 -0
- package/.medusa/server/src/modules/paypal/payment-provider/index.js +18 -0
- package/.medusa/server/src/modules/paypal/payment-provider/index.js.map +1 -0
- package/.medusa/server/src/modules/paypal/payment-provider/service.d.ts +39 -0
- package/.medusa/server/src/modules/paypal/payment-provider/service.d.ts.map +1 -0
- package/.medusa/server/src/modules/paypal/payment-provider/service.js +869 -0
- package/.medusa/server/src/modules/paypal/payment-provider/service.js.map +1 -0
- package/.medusa/server/src/modules/paypal/payment-provider/webhook-utils.d.ts +3 -0
- package/.medusa/server/src/modules/paypal/payment-provider/webhook-utils.d.ts.map +1 -0
- package/.medusa/server/src/modules/paypal/payment-provider/webhook-utils.js +74 -0
- package/.medusa/server/src/modules/paypal/payment-provider/webhook-utils.js.map +1 -0
- package/.medusa/server/src/modules/paypal/service.d.ts +266 -0
- package/.medusa/server/src/modules/paypal/service.d.ts.map +1 -0
- package/.medusa/server/src/modules/paypal/service.js +1149 -0
- package/.medusa/server/src/modules/paypal/service.js.map +1 -0
- package/.medusa/server/src/modules/paypal/types/config.d.ts +12 -0
- package/.medusa/server/src/modules/paypal/types/config.d.ts.map +1 -0
- package/.medusa/server/src/modules/paypal/types/config.js +24 -0
- package/.medusa/server/src/modules/paypal/types/config.js.map +1 -0
- package/.medusa/server/src/modules/paypal/utils/amounts.d.ts +3 -0
- package/.medusa/server/src/modules/paypal/utils/amounts.d.ts.map +1 -0
- package/.medusa/server/src/modules/paypal/utils/amounts.js +40 -0
- package/.medusa/server/src/modules/paypal/utils/amounts.js.map +1 -0
- package/.medusa/server/src/modules/paypal/utils/currencies.d.ts +19 -0
- package/.medusa/server/src/modules/paypal/utils/currencies.d.ts.map +1 -0
- package/.medusa/server/src/modules/paypal/utils/currencies.js +69 -0
- package/.medusa/server/src/modules/paypal/utils/currencies.js.map +1 -0
- package/.medusa/server/src/modules/paypal/utils/paypal-auth.d.ts +10 -0
- package/.medusa/server/src/modules/paypal/utils/paypal-auth.d.ts.map +1 -0
- package/.medusa/server/src/modules/paypal/utils/paypal-auth.js +30 -0
- package/.medusa/server/src/modules/paypal/utils/paypal-auth.js.map +1 -0
- package/.medusa/server/src/modules/paypal/utils/provider-ids.d.ts +5 -0
- package/.medusa/server/src/modules/paypal/utils/provider-ids.d.ts.map +1 -0
- package/.medusa/server/src/modules/paypal/utils/provider-ids.js +16 -0
- package/.medusa/server/src/modules/paypal/utils/provider-ids.js.map +1 -0
- package/.medusa/server/src/modules/paypal/webhook-processor.d.ts +29 -0
- package/.medusa/server/src/modules/paypal/webhook-processor.d.ts.map +1 -0
- package/.medusa/server/src/modules/paypal/webhook-processor.js +267 -0
- package/.medusa/server/src/modules/paypal/webhook-processor.js.map +1 -0
- package/.medusa/server/src/providers/paypal/index.d.ts +5 -0
- package/.medusa/server/src/providers/paypal/index.d.ts.map +1 -0
- package/.medusa/server/src/providers/paypal/index.js +10 -0
- package/.medusa/server/src/providers/paypal/index.js.map +1 -0
- package/.medusa/server/src/providers/paypal_card/index.d.ts +5 -0
- package/.medusa/server/src/providers/paypal_card/index.d.ts.map +1 -0
- package/.medusa/server/src/providers/paypal_card/index.js +10 -0
- package/.medusa/server/src/providers/paypal_card/index.js.map +1 -0
- package/.medusa/server/src/subscribers/paypal-order-invoice.d.ts +6 -0
- package/.medusa/server/src/subscribers/paypal-order-invoice.d.ts.map +1 -0
- package/.medusa/server/src/subscribers/paypal-order-invoice.js +124 -0
- package/.medusa/server/src/subscribers/paypal-order-invoice.js.map +1 -0
- package/LICENSE +21 -0
- package/README.md +172 -0
- package/package.json +80 -0
- package/src/admin/index.ts +7 -0
- package/src/admin/routes/settings/paypal/_components/Tabs.tsx +48 -0
- package/src/admin/routes/settings/paypal/_components/Toast.tsx +51 -0
- package/src/admin/routes/settings/paypal/additional-settings/page.tsx +199 -0
- package/src/admin/routes/settings/paypal/advanced-card-payments/page.tsx +183 -0
- package/src/admin/routes/settings/paypal/apple-pay/page.tsx +5 -0
- package/src/admin/routes/settings/paypal/connection/page.tsx +699 -0
- package/src/admin/routes/settings/paypal/google-pay/page.tsx +5 -0
- package/src/admin/routes/settings/paypal/page.tsx +13 -0
- package/src/admin/routes/settings/paypal/pay-later-messaging/page.tsx +5 -0
- package/src/admin/routes/settings/paypal/paypal-settings/page.tsx +351 -0
- package/src/api/admin/payment-collections/[id]/payment-sessions/route.ts +24 -0
- package/src/api/admin/paypal/disconnect/route.ts +8 -0
- package/src/api/admin/paypal/environment/route.ts +25 -0
- package/src/api/admin/paypal/onboard-complete/route.ts +34 -0
- package/src/api/admin/paypal/onboarding-link/route.ts +45 -0
- package/src/api/admin/paypal/onboarding-status/route.ts +17 -0
- package/src/api/admin/paypal/save-credentials/route.ts +22 -0
- package/src/api/admin/paypal/settings/route.ts +14 -0
- package/src/api/admin/paypal/status/route.ts +12 -0
- package/src/api/middlewares.ts +14 -0
- package/src/api/store/payment-collections/[id]/payment-sessions/route.ts +56 -0
- package/src/api/store/paypal/capture-order/route.ts +265 -0
- package/src/api/store/paypal/config/route.ts +100 -0
- package/src/api/store/paypal/create-order/route.ts +449 -0
- package/src/api/store/paypal/settings/route.ts +19 -0
- package/src/api/store/paypal/webhook/route.ts +325 -0
- package/src/api/store/paypal-complete/route.ts +95 -0
- package/src/index.ts +1 -0
- package/src/jobs/paypal-webhook-retry.ts +149 -0
- package/src/modules/paypal/clients/paypal-seller.client.ts +62 -0
- package/src/modules/paypal/index.ts +8 -0
- package/src/modules/paypal/migrations/20260115120000_create_paypal_connection.ts +33 -0
- package/src/modules/paypal/migrations/20260123090000_create_paypal_settings.ts +22 -0
- package/src/modules/paypal/migrations/20260201090000_create_paypal_webhook_event.ts +29 -0
- package/src/modules/paypal/migrations/20260401090000_create_paypal_metric.ts +27 -0
- package/src/modules/paypal/migrations/20260701090000_add_paypal_webhook_event_processing.ts +31 -0
- package/src/modules/paypal/migrations/20261101090000_remove_paypal_reconciliation_status.ts +25 -0
- package/src/modules/paypal/migrations/20261201090000_remove_paypal_audit_log.ts +26 -0
- package/src/modules/paypal/migrations/20270101090000_set_paypal_environment_default_live.ts +11 -0
- package/src/modules/paypal/migrations/20270102090000_add_paypal_seller_email.ts +11 -0
- package/src/modules/paypal/migrations/20270103090000_add_paypal_seller_merchant_id.ts +11 -0
- package/src/modules/paypal/migrations/20270201000000_add_webhook_dead_letter.ts +17 -0
- package/src/modules/paypal/models/paypal_connection.ts +23 -0
- package/src/modules/paypal/models/paypal_metric.ts +9 -0
- package/src/modules/paypal/models/paypal_settings.ts +8 -0
- package/src/modules/paypal/models/paypal_webhook_event.ts +19 -0
- package/src/modules/paypal/payment-provider/README.md +22 -0
- package/src/modules/paypal/payment-provider/card-service.ts +767 -0
- package/src/modules/paypal/payment-provider/index.ts +15 -0
- package/src/modules/paypal/payment-provider/service.ts +1039 -0
- package/src/modules/paypal/payment-provider/webhook-utils.ts +88 -0
- package/src/modules/paypal/service.ts +1416 -0
- package/src/modules/paypal/types/config.ts +33 -0
- package/src/modules/paypal/utils/amounts.ts +41 -0
- package/src/modules/paypal/utils/currencies.ts +84 -0
- package/src/modules/paypal/utils/paypal-auth.ts +31 -0
- package/src/modules/paypal/utils/provider-ids.ts +15 -0
- package/src/modules/paypal/webhook-processor.ts +363 -0
- package/src/providers/paypal/index.ts +8 -0
- package/src/providers/paypal_card/index.ts +8 -0
- package/src/subscribers/paypal-order-invoice.ts +171 -0
- package/tsconfig.json +31 -0
|
@@ -0,0 +1,1416 @@
|
|
|
1
|
+
import { MedusaService } from "@medusajs/framework/utils"
|
|
2
|
+
import PayPalConnection from "./models/paypal_connection"
|
|
3
|
+
import PayPalMetric from "./models/paypal_metric"
|
|
4
|
+
import PayPalSettings from "./models/paypal_settings"
|
|
5
|
+
import PayPalWebhookEvent from "./models/paypal_webhook_event"
|
|
6
|
+
import { getPayPalConfig } from "./types/config"
|
|
7
|
+
import { normalizeCurrencyCode } from "./utils/currencies"
|
|
8
|
+
|
|
9
|
+
type Environment = "sandbox" | "live"
|
|
10
|
+
|
|
11
|
+
type Status =
|
|
12
|
+
| "disconnected"
|
|
13
|
+
| "pending"
|
|
14
|
+
| "pending_credentials"
|
|
15
|
+
| "connected"
|
|
16
|
+
| "revoked"
|
|
17
|
+
|
|
18
|
+
class PayPalModuleService extends MedusaService({
|
|
19
|
+
PayPalConnection,
|
|
20
|
+
PayPalMetric,
|
|
21
|
+
PayPalSettings,
|
|
22
|
+
PayPalWebhookEvent,
|
|
23
|
+
}) {
|
|
24
|
+
protected cfg = getPayPalConfig()
|
|
25
|
+
|
|
26
|
+
private get bnCode(): string {
|
|
27
|
+
return this.cfg.bnCode || "MBJTechnolabs_SI_SPB"
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
private async getSettingsData() {
|
|
31
|
+
const settings = await this.getSettings()
|
|
32
|
+
return (settings?.data || {}) as Record<string, any>
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
private async ensureSettingsDefaults() {
|
|
36
|
+
const data = await this.getSettingsData()
|
|
37
|
+
const onboarding = { ...(data.onboarding_config || {}) } as Record<string, any>
|
|
38
|
+
const apiDetails = { ...(data.api_details || {}) } as Record<string, any>
|
|
39
|
+
let changed = false
|
|
40
|
+
|
|
41
|
+
if (!onboarding.partner_service_url) {
|
|
42
|
+
onboarding.partner_service_url = this.cfg.partnerServiceUrl
|
|
43
|
+
changed = true
|
|
44
|
+
}
|
|
45
|
+
if (!onboarding.partner_js_url) {
|
|
46
|
+
onboarding.partner_js_url = this.cfg.partnerJsUrl
|
|
47
|
+
changed = true
|
|
48
|
+
}
|
|
49
|
+
if (!onboarding.backend_url) {
|
|
50
|
+
onboarding.backend_url = this.cfg.backendUrl
|
|
51
|
+
changed = true
|
|
52
|
+
}
|
|
53
|
+
if (!onboarding.seller_nonce) {
|
|
54
|
+
onboarding.seller_nonce = this.cfg.sellerNonce
|
|
55
|
+
changed = true
|
|
56
|
+
}
|
|
57
|
+
if (!onboarding.bn_code && this.cfg.bnCode) {
|
|
58
|
+
onboarding.bn_code = this.cfg.bnCode
|
|
59
|
+
changed = true
|
|
60
|
+
}
|
|
61
|
+
if (!onboarding.partner_merchant_id_sandbox) {
|
|
62
|
+
onboarding.partner_merchant_id_sandbox = this.cfg.partnerMerchantIdSandbox
|
|
63
|
+
changed = true
|
|
64
|
+
}
|
|
65
|
+
if (!onboarding.partner_merchant_id_live) {
|
|
66
|
+
onboarding.partner_merchant_id_live = this.cfg.partnerMerchantIdLive
|
|
67
|
+
changed = true
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (!apiDetails.currency_code) {
|
|
71
|
+
const raw = (process.env.PAYPAL_CURRENCY || "").trim()
|
|
72
|
+
apiDetails.currency_code = raw ? normalizeCurrencyCode(raw) : "EUR"
|
|
73
|
+
changed = true
|
|
74
|
+
}
|
|
75
|
+
if (!apiDetails.storefront_url) {
|
|
76
|
+
const storeUrl = process.env.STOREFRONT_URL || process.env.STORE_URL
|
|
77
|
+
if (storeUrl) {
|
|
78
|
+
apiDetails.storefront_url = storeUrl
|
|
79
|
+
changed = true
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (changed) {
|
|
84
|
+
await this.saveSettings({
|
|
85
|
+
onboarding_config: onboarding,
|
|
86
|
+
api_details: apiDetails,
|
|
87
|
+
})
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return { onboarding, apiDetails }
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
async getApiDetails() {
|
|
94
|
+
const { onboarding, apiDetails } = await this.ensureSettingsDefaults()
|
|
95
|
+
return {
|
|
96
|
+
onboarding,
|
|
97
|
+
apiDetails,
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
private getAlertWebhookUrls() {
|
|
102
|
+
return (this.cfg.alertWebhookUrls || []).map((url) => url.trim()).filter(Boolean)
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
private async getPartnerMerchantId(env: Environment) {
|
|
106
|
+
const { onboarding } = await this.ensureSettingsDefaults()
|
|
107
|
+
return env === "live" ? onboarding.partner_merchant_id_live : onboarding.partner_merchant_id_sandbox
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
private async getCurrentRow(): Promise<any | null> {
|
|
111
|
+
const rows = await this.listPayPalConnections({})
|
|
112
|
+
return rows?.[0] ?? null
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
private async getCurrentEnvironment(): Promise<Environment> {
|
|
116
|
+
try {
|
|
117
|
+
const row = await this.getCurrentRow()
|
|
118
|
+
const env = (row?.environment as Environment) || "live"
|
|
119
|
+
return env === "sandbox" ? "sandbox" : "live"
|
|
120
|
+
} catch {
|
|
121
|
+
return "live"
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
private getEnvCreds(
|
|
126
|
+
row: any,
|
|
127
|
+
env: Environment
|
|
128
|
+
): { clientId?: string; clientSecret?: string; sellerMerchantId?: string; sellerEmail?: string } {
|
|
129
|
+
const meta = (row?.metadata || {}) as any
|
|
130
|
+
const creds = meta?.credentials?.[env] || {}
|
|
131
|
+
return {
|
|
132
|
+
clientId: creds.client_id || creds.clientId || undefined,
|
|
133
|
+
clientSecret: creds.client_secret || creds.clientSecret || undefined,
|
|
134
|
+
sellerMerchantId:
|
|
135
|
+
creds.seller_merchant_id ||
|
|
136
|
+
creds.sellerMerchantId ||
|
|
137
|
+
creds.payer_id ||
|
|
138
|
+
creds.merchant_id ||
|
|
139
|
+
creds.merchantId ||
|
|
140
|
+
undefined,
|
|
141
|
+
sellerEmail: creds.seller_email || creds.sellerEmail || undefined,
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
private extractSellerEmail(...candidates: any[]): string | null {
|
|
146
|
+
const queue = [...candidates]
|
|
147
|
+
|
|
148
|
+
while (queue.length > 0) {
|
|
149
|
+
const value = queue.shift()
|
|
150
|
+
if (!value) {
|
|
151
|
+
continue
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (typeof value === "string") {
|
|
155
|
+
const trimmed = value.trim()
|
|
156
|
+
if (trimmed && trimmed.includes("@")) {
|
|
157
|
+
return trimmed
|
|
158
|
+
}
|
|
159
|
+
continue
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
if (Array.isArray(value)) {
|
|
163
|
+
queue.push(...value)
|
|
164
|
+
continue
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
if (typeof value === "object") {
|
|
168
|
+
const obj = value as Record<string, any>
|
|
169
|
+
const prioritized = [
|
|
170
|
+
obj.email,
|
|
171
|
+
obj.primary_email,
|
|
172
|
+
obj.merchant_email,
|
|
173
|
+
obj.email_address,
|
|
174
|
+
obj.account_email,
|
|
175
|
+
obj.contact_email,
|
|
176
|
+
obj.value,
|
|
177
|
+
obj.address,
|
|
178
|
+
]
|
|
179
|
+
queue.push(...prioritized)
|
|
180
|
+
|
|
181
|
+
for (const [k, v] of Object.entries(obj)) {
|
|
182
|
+
const key = String(k).toLowerCase()
|
|
183
|
+
if (key.includes("email") || key.includes("address")) {
|
|
184
|
+
queue.push(v)
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
queue.push(...Object.values(obj))
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
return null
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
private async fetchMerchantIntegrationDetails(
|
|
196
|
+
env: Environment,
|
|
197
|
+
merchantId: string,
|
|
198
|
+
accessTokenOverride?: string
|
|
199
|
+
) {
|
|
200
|
+
const partnerMerchantId = await this.getPartnerMerchantId(env)
|
|
201
|
+
if (!partnerMerchantId) {
|
|
202
|
+
throw new Error("Missing PayPal partner merchant id configuration.")
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
const baseUrl = env === "live" ? "https://api-m.paypal.com" : "https://api-m.sandbox.paypal.com"
|
|
206
|
+
const accessToken = accessTokenOverride ?? await this.getAppAccessToken()
|
|
207
|
+
|
|
208
|
+
const resp = await fetch(
|
|
209
|
+
`${baseUrl}/v1/customer/partners/${encodeURIComponent(
|
|
210
|
+
partnerMerchantId
|
|
211
|
+
)}/merchant-integrations/${encodeURIComponent(merchantId)}`,
|
|
212
|
+
{
|
|
213
|
+
method: "GET",
|
|
214
|
+
headers: {
|
|
215
|
+
"Content-Type": "application/json",
|
|
216
|
+
Authorization: `Bearer ${accessToken}`,
|
|
217
|
+
"PayPal-Partner-Attribution-Id": this.bnCode,
|
|
218
|
+
},
|
|
219
|
+
}
|
|
220
|
+
)
|
|
221
|
+
|
|
222
|
+
const text = await resp.text().catch(() => "")
|
|
223
|
+
let json: any = {}
|
|
224
|
+
try {
|
|
225
|
+
json = text ? JSON.parse(text) : {}
|
|
226
|
+
} catch (e: any) {
|
|
227
|
+
console.warn("[PayPal] Failed to parse response JSON — using empty object:", e?.message)
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
if (!resp.ok) {
|
|
231
|
+
throw new Error(
|
|
232
|
+
`PayPal merchant integration lookup failed (${resp.status}): ${text || JSON.stringify(json)}`
|
|
233
|
+
)
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
return json
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
private async getAppAccessTokenForCredentials(
|
|
240
|
+
env: Environment,
|
|
241
|
+
credentials: { clientId: string; clientSecret: string }
|
|
242
|
+
) {
|
|
243
|
+
const baseUrl = env === "live" ? "https://api-m.paypal.com" : "https://api-m.sandbox.paypal.com"
|
|
244
|
+
const basic = Buffer.from(`${credentials.clientId}:${credentials.clientSecret}`).toString("base64")
|
|
245
|
+
|
|
246
|
+
const body = new URLSearchParams()
|
|
247
|
+
body.set("grant_type", "client_credentials")
|
|
248
|
+
|
|
249
|
+
const res = await fetch(`${baseUrl}/v1/oauth2/token`, {
|
|
250
|
+
method: "POST",
|
|
251
|
+
headers: {
|
|
252
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
253
|
+
Authorization: `Basic ${basic}`,
|
|
254
|
+
"PayPal-Partner-Attribution-Id": this.bnCode,
|
|
255
|
+
},
|
|
256
|
+
body,
|
|
257
|
+
})
|
|
258
|
+
|
|
259
|
+
const text = await res.text().catch(() => "")
|
|
260
|
+
let json: any = {}
|
|
261
|
+
try {
|
|
262
|
+
json = text ? JSON.parse(text) : {}
|
|
263
|
+
} catch (e: any) {
|
|
264
|
+
console.warn("[PayPal] Failed to parse app token response JSON:", e?.message)
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
if (!res.ok) {
|
|
268
|
+
throw new Error(`PayPal client_credentials failed (${res.status}): ${text || JSON.stringify(json)}`)
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
const accessToken = String(json.access_token || "")
|
|
272
|
+
if (!accessToken) {
|
|
273
|
+
throw new Error("PayPal client_credentials succeeded but access_token is missing.")
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
return { accessToken, tokenPayload: json }
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
private async fetchSellerProfileFromDirectCredentials(
|
|
280
|
+
env: Environment,
|
|
281
|
+
credentials?: { clientId: string; clientSecret: string }
|
|
282
|
+
): Promise<{ sellerMerchantId: string | null; sellerEmail: string | null }> {
|
|
283
|
+
const baseUrl = env === "live" ? "https://api-m.paypal.com" : "https://api-m.sandbox.paypal.com"
|
|
284
|
+
const partnerMerchantId = await this.getPartnerMerchantId(env)
|
|
285
|
+
let tokenPayload: Record<string, any> | null = null
|
|
286
|
+
|
|
287
|
+
let accessToken = ""
|
|
288
|
+
if (credentials) {
|
|
289
|
+
const tokenResp = await this.getAppAccessTokenForCredentials(env, {
|
|
290
|
+
clientId: credentials.clientId,
|
|
291
|
+
clientSecret: credentials.clientSecret,
|
|
292
|
+
})
|
|
293
|
+
accessToken = tokenResp.accessToken
|
|
294
|
+
tokenPayload = tokenResp.tokenPayload
|
|
295
|
+
} else {
|
|
296
|
+
accessToken = await this.getAppAccessToken()
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
let sellerEmail = this.extractSellerEmail(tokenPayload || undefined)
|
|
300
|
+
let sellerMerchantId = String(
|
|
301
|
+
tokenPayload?.merchant_id || tokenPayload?.payer_id || tokenPayload?.account_id || ""
|
|
302
|
+
).trim() || null
|
|
303
|
+
|
|
304
|
+
try {
|
|
305
|
+
const userInfoResp = await fetch(`${baseUrl}/v1/identity/oauth2/userinfo?schema=paypalv1`, {
|
|
306
|
+
method: "GET",
|
|
307
|
+
headers: {
|
|
308
|
+
Authorization: `Bearer ${accessToken}`,
|
|
309
|
+
"Content-Type": "application/json",
|
|
310
|
+
Accept: "application/json",
|
|
311
|
+
"PayPal-Partner-Attribution-Id": this.bnCode,
|
|
312
|
+
},
|
|
313
|
+
})
|
|
314
|
+
if (userInfoResp.ok) {
|
|
315
|
+
const userInfo = await userInfoResp.json().catch(() => ({}))
|
|
316
|
+
sellerEmail = sellerEmail || this.extractSellerEmail(userInfo)
|
|
317
|
+
sellerMerchantId =
|
|
318
|
+
sellerMerchantId ||
|
|
319
|
+
String(userInfo?.merchant_id || userInfo?.payer_id || userInfo?.user_id || userInfo?.sub || "").trim() ||
|
|
320
|
+
null
|
|
321
|
+
}
|
|
322
|
+
} catch (e: any) {
|
|
323
|
+
console.warn("[PayPal] userinfo lookup failed:", e?.message || e)
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
if (partnerMerchantId) {
|
|
327
|
+
try {
|
|
328
|
+
const credsResp = await fetch(
|
|
329
|
+
`${baseUrl}/v1/customer/partners/${encodeURIComponent(
|
|
330
|
+
partnerMerchantId
|
|
331
|
+
)}/merchant-integrations/credentials/`,
|
|
332
|
+
{
|
|
333
|
+
method: "GET",
|
|
334
|
+
headers: {
|
|
335
|
+
Authorization: `Bearer ${accessToken}`,
|
|
336
|
+
"Content-Type": "application/json",
|
|
337
|
+
"PayPal-Partner-Attribution-Id": this.bnCode,
|
|
338
|
+
},
|
|
339
|
+
}
|
|
340
|
+
)
|
|
341
|
+
if (credsResp.ok) {
|
|
342
|
+
const credsJson = await credsResp.json().catch(() => ({}))
|
|
343
|
+
sellerEmail = sellerEmail || this.extractSellerEmail(credsJson)
|
|
344
|
+
sellerMerchantId = sellerMerchantId || String(credsJson?.merchant_id || "").trim() || null
|
|
345
|
+
}
|
|
346
|
+
} catch (e: any) {
|
|
347
|
+
console.warn("[PayPal] direct credential profile lookup failed:", e?.message || e)
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
const hydrated = await this.hydrateSellerMetadataFromCredentials(env, {
|
|
352
|
+
accessToken,
|
|
353
|
+
sellerMerchantId,
|
|
354
|
+
sellerEmail,
|
|
355
|
+
})
|
|
356
|
+
|
|
357
|
+
return {
|
|
358
|
+
sellerMerchantId: hydrated.sellerMerchantId,
|
|
359
|
+
sellerEmail: hydrated.sellerEmail,
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
private async hydrateSellerMetadataFromCredentials(
|
|
364
|
+
env: Environment,
|
|
365
|
+
input: {
|
|
366
|
+
accessToken?: string
|
|
367
|
+
sellerMerchantId?: string | null
|
|
368
|
+
sellerEmail?: string | null
|
|
369
|
+
metadataCandidates?: any[]
|
|
370
|
+
}
|
|
371
|
+
): Promise<{ sellerMerchantId: string | null; sellerEmail: string | null }> {
|
|
372
|
+
let sellerMerchantId = (input.sellerMerchantId || "").trim() || null
|
|
373
|
+
let sellerEmail = (input.sellerEmail || "").trim() || null
|
|
374
|
+
|
|
375
|
+
if (!sellerEmail && input.metadataCandidates?.length) {
|
|
376
|
+
sellerEmail = this.extractSellerEmail(...input.metadataCandidates)
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
if (sellerMerchantId && !sellerEmail) {
|
|
380
|
+
try {
|
|
381
|
+
const details = await this.fetchMerchantIntegrationDetails(env, sellerMerchantId, input.accessToken)
|
|
382
|
+
sellerEmail = this.extractSellerEmail(details)
|
|
383
|
+
} catch (e: any) {
|
|
384
|
+
console.warn("[PayPal] merchant integration lookup failed:", e?.message || e)
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
return { sellerMerchantId, sellerEmail }
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
private async syncRowFieldsFromMetadata(row: any, env: Environment) {
|
|
392
|
+
const c = this.getEnvCreds(row, env)
|
|
393
|
+
await this.updatePayPalConnections({
|
|
394
|
+
id: row.id,
|
|
395
|
+
status: c.clientId && c.clientSecret ? "connected" : "disconnected",
|
|
396
|
+
seller_client_id: c.clientId || null,
|
|
397
|
+
seller_client_secret: c.clientSecret || null,
|
|
398
|
+
seller_merchant_id: c.sellerMerchantId || null,
|
|
399
|
+
seller_email: c.sellerEmail || null,
|
|
400
|
+
metadata: {
|
|
401
|
+
...(row.metadata || {}),
|
|
402
|
+
active_environment: env,
|
|
403
|
+
},
|
|
404
|
+
})
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
async setEnvironment(env: Environment) {
|
|
408
|
+
const nextEnv: Environment = env === "sandbox" ? "sandbox" : "live"
|
|
409
|
+
const row = await this.getCurrentRow()
|
|
410
|
+
const previousEnv = (row?.environment as Environment) || "live"
|
|
411
|
+
|
|
412
|
+
if (!row) {
|
|
413
|
+
const created = await this.createPayPalConnections({
|
|
414
|
+
environment: nextEnv,
|
|
415
|
+
status: "disconnected",
|
|
416
|
+
shared_id: null,
|
|
417
|
+
auth_code: null,
|
|
418
|
+
seller_client_id: null,
|
|
419
|
+
seller_client_secret: null,
|
|
420
|
+
seller_merchant_id: null,
|
|
421
|
+
seller_email: null,
|
|
422
|
+
app_access_token: null,
|
|
423
|
+
app_access_token_expires_at: null,
|
|
424
|
+
metadata: { credentials: {}, active_environment: nextEnv },
|
|
425
|
+
})
|
|
426
|
+
await this.recordAuditEvent("environment_switched", {
|
|
427
|
+
previous_environment: previousEnv,
|
|
428
|
+
environment: nextEnv,
|
|
429
|
+
})
|
|
430
|
+
return created
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
await this.updatePayPalConnections({
|
|
434
|
+
id: row.id,
|
|
435
|
+
environment: nextEnv,
|
|
436
|
+
app_access_token: null,
|
|
437
|
+
app_access_token_expires_at: null,
|
|
438
|
+
metadata: {
|
|
439
|
+
...(row.metadata || {}),
|
|
440
|
+
active_environment: nextEnv,
|
|
441
|
+
},
|
|
442
|
+
})
|
|
443
|
+
|
|
444
|
+
const updated = await this.getCurrentRow()
|
|
445
|
+
if (updated) {
|
|
446
|
+
await this.syncRowFieldsFromMetadata(updated, nextEnv)
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
await this.recordAuditEvent("environment_switched", {
|
|
450
|
+
previous_environment: previousEnv,
|
|
451
|
+
environment: nextEnv,
|
|
452
|
+
})
|
|
453
|
+
return await this.getCurrentRow()
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
async createOnboardingLink(input?: { email?: string; products?: string[] }) {
|
|
457
|
+
const { onboarding } = await this.ensureSettingsDefaults()
|
|
458
|
+
const return_url = `${String(onboarding.backend_url || "").replace(/\/$/, "")}/admin/paypal/onboard-complete`
|
|
459
|
+
const env = await this.getCurrentEnvironment()
|
|
460
|
+
const partner_merchant_id = await this.getPartnerMerchantId(env)
|
|
461
|
+
|
|
462
|
+
const email = (input?.email || "").trim()
|
|
463
|
+
|
|
464
|
+
if (!partner_merchant_id) {
|
|
465
|
+
throw new Error("Missing PAYPAL_PARTNER_MERCHANT_ID_* env for current environment")
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
const form = new URLSearchParams()
|
|
469
|
+
if (email) {
|
|
470
|
+
form.set("email", email)
|
|
471
|
+
}
|
|
472
|
+
form.set("sandbox", env === "live" ? "no" : "yes")
|
|
473
|
+
form.set("return_url", return_url)
|
|
474
|
+
form.set("return_url_description", "Return to your shop.")
|
|
475
|
+
form.set("partner_merchant_id", partner_merchant_id)
|
|
476
|
+
form.set("from", "medusa")
|
|
477
|
+
|
|
478
|
+
const products = input?.products?.length ? input.products : ["PPCP"]
|
|
479
|
+
|
|
480
|
+
products.forEach((p, i) => {
|
|
481
|
+
form.append(`products[${i}]`, p)
|
|
482
|
+
form.append("products[]", p)
|
|
483
|
+
})
|
|
484
|
+
|
|
485
|
+
const res = await fetch(onboarding.partner_service_url, {
|
|
486
|
+
method: "POST",
|
|
487
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
488
|
+
body: form.toString(),
|
|
489
|
+
})
|
|
490
|
+
|
|
491
|
+
const text = await res.text().catch(() => "")
|
|
492
|
+
if (!res.ok) {
|
|
493
|
+
throw new Error(`Onboarding service failed (${res.status}): ${text}`)
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
const trimmed = text.trim()
|
|
497
|
+
|
|
498
|
+
if (trimmed.startsWith("http")) {
|
|
499
|
+
return { onboarding_url: trimmed, return_url }
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
let json: any
|
|
503
|
+
try {
|
|
504
|
+
json = JSON.parse(trimmed)
|
|
505
|
+
} catch {
|
|
506
|
+
throw new Error(`Invalid onboarding link response (not JSON / URL): ${trimmed.slice(0, 200)}`)
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
if (json?.body) {
|
|
510
|
+
const inner = typeof json.body === "string" ? json.body.trim() : json.body
|
|
511
|
+
|
|
512
|
+
if (typeof inner === "string" && inner.startsWith("http")) {
|
|
513
|
+
return { onboarding_url: inner, return_url }
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
try {
|
|
517
|
+
json = typeof inner === "string" ? JSON.parse(inner) : inner
|
|
518
|
+
} catch {
|
|
519
|
+
throw new Error(
|
|
520
|
+
`Onboarding wrapper JSON 'body' is not valid JSON / URL: ${
|
|
521
|
+
typeof inner === "string" ? inner.slice(0, 200) : "[object]"
|
|
522
|
+
}`
|
|
523
|
+
)
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
if (json?.name && json?.message && (json?.debug_id || json?.details || json?.links)) {
|
|
528
|
+
const debug = json.debug_id ? ` debug_id=${json.debug_id}` : ""
|
|
529
|
+
const details = Array.isArray(json.details)
|
|
530
|
+
? json.details
|
|
531
|
+
.slice(0, 3)
|
|
532
|
+
.map((d: any) => {
|
|
533
|
+
const issue = d?.issue ? String(d.issue) : ""
|
|
534
|
+
const desc = d?.description ? String(d.description) : ""
|
|
535
|
+
const field = d?.field ? String(d.field) : ""
|
|
536
|
+
return [issue, desc, field].filter(Boolean).join(" | ")
|
|
537
|
+
})
|
|
538
|
+
.filter(Boolean)
|
|
539
|
+
.join("; ")
|
|
540
|
+
: ""
|
|
541
|
+
|
|
542
|
+
throw new Error(`PayPal onboarding error: ${json.name}: ${json.message}.${debug}${details ? ` Details: ${details}` : ""}`)
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
if (json?.onboarding_url && String(json.onboarding_url).startsWith("http")) {
|
|
546
|
+
return { onboarding_url: String(json.onboarding_url), return_url }
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
const links = Array.isArray(json?.links) ? json.links : null
|
|
550
|
+
if (links) {
|
|
551
|
+
const action = links.find(
|
|
552
|
+
(l: any) => l?.rel === "action_url" || l?.rel === "actionUrl" || l?.rel === "action-url"
|
|
553
|
+
)
|
|
554
|
+
const href = action?.href ? String(action.href) : null
|
|
555
|
+
if (href && href.startsWith("http")) {
|
|
556
|
+
return { onboarding_url: href, return_url }
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
throw new Error(
|
|
561
|
+
`Onboarding JSON missing action_url link. Keys: ${Object.keys(json || {}).join(", ")}`
|
|
562
|
+
)
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
async startOnboarding() {
|
|
566
|
+
const row = await this.getCurrentRow()
|
|
567
|
+
const env = await this.getCurrentEnvironment()
|
|
568
|
+
|
|
569
|
+
if (row) {
|
|
570
|
+
await this.updatePayPalConnections({ id: row.id, status: "pending" })
|
|
571
|
+
return
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
await this.createPayPalConnections({
|
|
575
|
+
environment: env,
|
|
576
|
+
status: "pending",
|
|
577
|
+
metadata: {},
|
|
578
|
+
})
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
async saveOnboardCallback(input: { authCode: string; sharedId: string }) {
|
|
582
|
+
const row = await this.getCurrentRow()
|
|
583
|
+
const env = await this.getCurrentEnvironment()
|
|
584
|
+
|
|
585
|
+
if (!row) {
|
|
586
|
+
return await this.createPayPalConnections({
|
|
587
|
+
environment: env,
|
|
588
|
+
status: "pending_credentials",
|
|
589
|
+
auth_code: input.authCode,
|
|
590
|
+
shared_id: input.sharedId,
|
|
591
|
+
metadata: {},
|
|
592
|
+
})
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
return await this.updatePayPalConnections({
|
|
596
|
+
id: row.id,
|
|
597
|
+
status: "pending_credentials",
|
|
598
|
+
auth_code: input.authCode,
|
|
599
|
+
shared_id: input.sharedId,
|
|
600
|
+
})
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
async exchangeAndSaveSellerCredentials(input: {
|
|
604
|
+
authCode: string
|
|
605
|
+
sharedId: string
|
|
606
|
+
env?: "sandbox" | "live"
|
|
607
|
+
}) {
|
|
608
|
+
await this.saveOnboardCallback({ authCode: input.authCode, sharedId: input.sharedId })
|
|
609
|
+
|
|
610
|
+
const env = (input.env || (await this.getCurrentEnvironment())) as Environment
|
|
611
|
+
const baseUrl = env === "live" ? "https://api-m.paypal.com" : "https://api-m.sandbox.paypal.com"
|
|
612
|
+
|
|
613
|
+
const { onboarding } = await this.ensureSettingsDefaults()
|
|
614
|
+
const sellerNonce = (onboarding.seller_nonce || "").trim()
|
|
615
|
+
if (!sellerNonce) {
|
|
616
|
+
throw new Error("PayPal seller nonce is not configured. Set PAYPAL_SELLER_NONCE.")
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
const tokenBody = new URLSearchParams()
|
|
620
|
+
tokenBody.set("grant_type", "authorization_code")
|
|
621
|
+
tokenBody.set("code", input.authCode)
|
|
622
|
+
tokenBody.set("code_verifier", sellerNonce)
|
|
623
|
+
|
|
624
|
+
const basic = Buffer.from(`${input.sharedId}:`).toString("base64")
|
|
625
|
+
|
|
626
|
+
const tokenRes = await fetch(`${baseUrl}/v1/oauth2/token`, {
|
|
627
|
+
method: "POST",
|
|
628
|
+
headers: {
|
|
629
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
630
|
+
Authorization: `Basic ${basic}`,
|
|
631
|
+
"PayPal-Partner-Attribution-Id": this.bnCode,
|
|
632
|
+
},
|
|
633
|
+
body: tokenBody,
|
|
634
|
+
})
|
|
635
|
+
|
|
636
|
+
const tokenText = await tokenRes.text().catch(() => "")
|
|
637
|
+
let tokenJson: any = {}
|
|
638
|
+
try {
|
|
639
|
+
tokenJson = tokenText ? JSON.parse(tokenText) : {}
|
|
640
|
+
} catch (e: any) {
|
|
641
|
+
console.warn("[PayPal] Failed to parse token response JSON:", e?.message)
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
if (!tokenRes.ok) {
|
|
645
|
+
throw new Error(
|
|
646
|
+
`PayPal authorization_code token exchange failed (${tokenRes.status}): ${tokenText || JSON.stringify(tokenJson)}`
|
|
647
|
+
)
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
const sellerAccessToken = String(tokenJson.access_token || "")
|
|
651
|
+
if (!sellerAccessToken) {
|
|
652
|
+
throw new Error("PayPal token exchange succeeded but access_token is missing.")
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
const partnerMerchantId = await this.getPartnerMerchantId(env)
|
|
656
|
+
if (!partnerMerchantId) {
|
|
657
|
+
throw new Error("Missing PayPal partner merchant id configuration.")
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
const credRes = await fetch(
|
|
661
|
+
`${baseUrl}/v1/customer/partners/${encodeURIComponent(partnerMerchantId)}/merchant-integrations/credentials/`,
|
|
662
|
+
{
|
|
663
|
+
method: "GET",
|
|
664
|
+
headers: {
|
|
665
|
+
"Content-Type": "application/json",
|
|
666
|
+
Authorization: `Bearer ${sellerAccessToken}`,
|
|
667
|
+
"PayPal-Partner-Attribution-Id": this.bnCode,
|
|
668
|
+
},
|
|
669
|
+
}
|
|
670
|
+
)
|
|
671
|
+
|
|
672
|
+
const credText = await credRes.text().catch(() => "")
|
|
673
|
+
let credJson: any = {}
|
|
674
|
+
try {
|
|
675
|
+
credJson = credText ? JSON.parse(credText) : {}
|
|
676
|
+
} catch (e: any) {
|
|
677
|
+
console.warn("[PayPal] Failed to parse token response JSON:", e?.message)
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
if (!credRes.ok) {
|
|
681
|
+
throw new Error(
|
|
682
|
+
`PayPal credentials fetch failed (${credRes.status}): ${credText || JSON.stringify(credJson)}`
|
|
683
|
+
)
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
const clientId = String(credJson.client_id || credJson.clientId || "")
|
|
687
|
+
const clientSecret = String(credJson.client_secret || credJson.clientSecret || "")
|
|
688
|
+
if (!clientId || !clientSecret) {
|
|
689
|
+
throw new Error(
|
|
690
|
+
`PayPal credentials response missing client_id/client_secret. Keys: ${Object.keys(credJson || {}).join(", ")}`
|
|
691
|
+
)
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
let sellerEmail = this.extractSellerEmail(credJson, tokenJson)
|
|
695
|
+
let sellerMerchantId =
|
|
696
|
+
String(credJson.payer_id || credJson.merchant_id || tokenJson.payer_id || tokenJson.merchant_id || "").trim() ||
|
|
697
|
+
null
|
|
698
|
+
|
|
699
|
+
if (!sellerEmail) {
|
|
700
|
+
const merchantCandidates = [
|
|
701
|
+
String(credJson.payer_id || "").trim(),
|
|
702
|
+
String(credJson.merchant_id || "").trim(),
|
|
703
|
+
String(tokenJson.payer_id || "").trim(),
|
|
704
|
+
String(tokenJson.merchant_id || "").trim(),
|
|
705
|
+
].filter(Boolean).filter((v, i, arr) => arr.indexOf(v) === i)
|
|
706
|
+
|
|
707
|
+
for (const merchantId of merchantCandidates) {
|
|
708
|
+
try {
|
|
709
|
+
const merchantDetails = await this.fetchMerchantIntegrationDetails(env, merchantId, sellerAccessToken)
|
|
710
|
+
sellerMerchantId = sellerMerchantId || merchantId
|
|
711
|
+
sellerEmail = this.extractSellerEmail(merchantDetails)
|
|
712
|
+
if (sellerEmail) {
|
|
713
|
+
break
|
|
714
|
+
}
|
|
715
|
+
} catch (e: any) {
|
|
716
|
+
console.warn(`[PayPal] Merchant integration lookup for ${merchantId} failed:`, e?.message || e)
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
await this.saveSellerCredentials({
|
|
722
|
+
clientId,
|
|
723
|
+
clientSecret,
|
|
724
|
+
sellerMerchantId,
|
|
725
|
+
sellerEmail,
|
|
726
|
+
})
|
|
727
|
+
|
|
728
|
+
if (!sellerEmail && sellerMerchantId) {
|
|
729
|
+
try {
|
|
730
|
+
const appAccessToken = await this.getAppAccessToken()
|
|
731
|
+
const details = await this.fetchMerchantIntegrationDetails(env, sellerMerchantId, appAccessToken)
|
|
732
|
+
sellerEmail = this.extractSellerEmail(details)
|
|
733
|
+
if (sellerEmail) {
|
|
734
|
+
await this.saveSellerCredentials({ clientId, clientSecret, sellerMerchantId, sellerEmail })
|
|
735
|
+
}
|
|
736
|
+
} catch (e: any) {
|
|
737
|
+
console.warn("[PayPal] Post-save merchant_id email lookup failed:", e?.message || e)
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
async saveSellerCredentials(input: {
|
|
743
|
+
clientId: string
|
|
744
|
+
clientSecret: string
|
|
745
|
+
sellerMerchantId?: string | null
|
|
746
|
+
sellerEmail?: string | null
|
|
747
|
+
environment?: Environment
|
|
748
|
+
}) {
|
|
749
|
+
const row = await this.getCurrentRow()
|
|
750
|
+
const currentEnv = await this.getCurrentEnvironment()
|
|
751
|
+
const env = (input.environment || currentEnv) as Environment
|
|
752
|
+
|
|
753
|
+
const existingCreds = row ? this.getEnvCreds(row, env) : {}
|
|
754
|
+
const nextSellerMerchantId =
|
|
755
|
+
(input.sellerMerchantId || "").trim() || existingCreds.sellerMerchantId || row?.seller_merchant_id || null
|
|
756
|
+
const nextSellerEmail =
|
|
757
|
+
(input.sellerEmail || "").trim() || existingCreds.sellerEmail || row?.seller_email || null
|
|
758
|
+
|
|
759
|
+
const nextCreds = {
|
|
760
|
+
client_id: input.clientId,
|
|
761
|
+
clientId: input.clientId,
|
|
762
|
+
client_secret: input.clientSecret,
|
|
763
|
+
clientSecret: input.clientSecret,
|
|
764
|
+
merchantId: nextSellerMerchantId,
|
|
765
|
+
merchant_id: nextSellerMerchantId,
|
|
766
|
+
payer_id: nextSellerMerchantId,
|
|
767
|
+
seller_merchant_id: nextSellerMerchantId,
|
|
768
|
+
sellerMerchantId: nextSellerMerchantId,
|
|
769
|
+
seller_email: nextSellerEmail,
|
|
770
|
+
sellerEmail: nextSellerEmail,
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
if (!row) {
|
|
774
|
+
const created = await this.createPayPalConnections({
|
|
775
|
+
environment: env,
|
|
776
|
+
status: "connected",
|
|
777
|
+
seller_client_id: input.clientId,
|
|
778
|
+
seller_client_secret: input.clientSecret,
|
|
779
|
+
seller_merchant_id: nextSellerMerchantId,
|
|
780
|
+
seller_email: nextSellerEmail,
|
|
781
|
+
app_access_token: null,
|
|
782
|
+
app_access_token_expires_at: null,
|
|
783
|
+
metadata: {
|
|
784
|
+
credentials: {
|
|
785
|
+
[env]: nextCreds,
|
|
786
|
+
},
|
|
787
|
+
active_environment: env,
|
|
788
|
+
},
|
|
789
|
+
})
|
|
790
|
+
await this.recordAuditEvent("credentials_saved", {
|
|
791
|
+
environment: env,
|
|
792
|
+
client_id: input.clientId,
|
|
793
|
+
})
|
|
794
|
+
await this.ensureWebhookRegistration()
|
|
795
|
+
return created
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
const meta = (row.metadata || {}) as any
|
|
799
|
+
const creds = { ...(meta.credentials || {}) }
|
|
800
|
+
creds[env] = {
|
|
801
|
+
...(creds[env] || {}),
|
|
802
|
+
...nextCreds,
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
const updated = await this.updatePayPalConnections({
|
|
806
|
+
id: row.id,
|
|
807
|
+
status: "connected",
|
|
808
|
+
seller_client_id: input.clientId,
|
|
809
|
+
seller_client_secret: input.clientSecret,
|
|
810
|
+
seller_merchant_id: nextSellerMerchantId,
|
|
811
|
+
seller_email: nextSellerEmail,
|
|
812
|
+
app_access_token: null,
|
|
813
|
+
app_access_token_expires_at: null,
|
|
814
|
+
metadata: {
|
|
815
|
+
...(row.metadata || {}),
|
|
816
|
+
credentials: creds,
|
|
817
|
+
active_environment: env,
|
|
818
|
+
},
|
|
819
|
+
})
|
|
820
|
+
await this.recordAuditEvent("credentials_saved", {
|
|
821
|
+
environment: env,
|
|
822
|
+
client_id: input.clientId,
|
|
823
|
+
})
|
|
824
|
+
await this.ensureWebhookRegistration()
|
|
825
|
+
return updated
|
|
826
|
+
}
|
|
827
|
+
|
|
828
|
+
async saveAndHydrateSellerCredentials(input: {
|
|
829
|
+
clientId: string
|
|
830
|
+
clientSecret: string
|
|
831
|
+
environment?: Environment
|
|
832
|
+
}) {
|
|
833
|
+
const env = (input.environment || (await this.getCurrentEnvironment())) as Environment
|
|
834
|
+
|
|
835
|
+
await this.saveSellerCredentials({
|
|
836
|
+
clientId: input.clientId,
|
|
837
|
+
clientSecret: input.clientSecret,
|
|
838
|
+
environment: env,
|
|
839
|
+
})
|
|
840
|
+
|
|
841
|
+
try {
|
|
842
|
+
const hydrated = await this.fetchSellerProfileFromDirectCredentials(env, {
|
|
843
|
+
clientId: input.clientId,
|
|
844
|
+
clientSecret: input.clientSecret,
|
|
845
|
+
})
|
|
846
|
+
|
|
847
|
+
if (hydrated.sellerEmail || hydrated.sellerMerchantId) {
|
|
848
|
+
await this.saveSellerCredentials({
|
|
849
|
+
clientId: input.clientId,
|
|
850
|
+
clientSecret: input.clientSecret,
|
|
851
|
+
sellerMerchantId: hydrated.sellerMerchantId,
|
|
852
|
+
sellerEmail: hydrated.sellerEmail,
|
|
853
|
+
environment: env,
|
|
854
|
+
})
|
|
855
|
+
}
|
|
856
|
+
|
|
857
|
+
const refreshedRow = await this.getCurrentRow()
|
|
858
|
+
if (refreshedRow) {
|
|
859
|
+
await this.syncRowFieldsFromMetadata(refreshedRow, env)
|
|
860
|
+
}
|
|
861
|
+
} catch (e: any) {
|
|
862
|
+
console.warn("[PayPal] saveAndHydrateSellerCredentials lookup failed:", e?.message || e)
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
return await this.getStatus(env)
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
private async resolveWebhookUrl() {
|
|
869
|
+
const { onboarding } = await this.ensureSettingsDefaults()
|
|
870
|
+
const base = String(onboarding.backend_url || "").replace(/\/$/, "")
|
|
871
|
+
if (!base) {
|
|
872
|
+
throw new Error("PayPal backend URL is not configured.")
|
|
873
|
+
}
|
|
874
|
+
return `${base}/store/paypal/webhook`
|
|
875
|
+
}
|
|
876
|
+
|
|
877
|
+
private isLocalWebhookUrl(url: string) {
|
|
878
|
+
try {
|
|
879
|
+
const parsed = new URL(url)
|
|
880
|
+
return ["localhost", "127.0.0.1", "::1"].includes(parsed.hostname)
|
|
881
|
+
} catch {
|
|
882
|
+
return false
|
|
883
|
+
}
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
private async ensureWebhookRegistration() {
|
|
887
|
+
const env = await this.getCurrentEnvironment()
|
|
888
|
+
const { apiDetails } = await this.ensureSettingsDefaults()
|
|
889
|
+
const webhookIds = { ...(apiDetails.webhook_ids || {}) } as Record<string, string>
|
|
890
|
+
|
|
891
|
+
if (webhookIds[env]) {
|
|
892
|
+
return webhookIds[env]
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
const accessToken = await this.getAppAccessToken()
|
|
896
|
+
const baseUrl = env === "live" ? "https://api-m.paypal.com" : "https://api-m.sandbox.paypal.com"
|
|
897
|
+
const webhookUrl = await this.resolveWebhookUrl()
|
|
898
|
+
|
|
899
|
+
if (this.isLocalWebhookUrl(webhookUrl)) {
|
|
900
|
+
await this.recordAuditEvent("webhook_skipped_localhost", {
|
|
901
|
+
environment: env,
|
|
902
|
+
webhook_url: webhookUrl,
|
|
903
|
+
})
|
|
904
|
+
return webhookIds[env] || ""
|
|
905
|
+
}
|
|
906
|
+
|
|
907
|
+
const listResp = await fetch(`${baseUrl}/v1/notifications/webhooks`, {
|
|
908
|
+
headers: {
|
|
909
|
+
Authorization: `Bearer ${accessToken}`,
|
|
910
|
+
"Content-Type": "application/json",
|
|
911
|
+
"PayPal-Partner-Attribution-Id": this.bnCode,
|
|
912
|
+
},
|
|
913
|
+
})
|
|
914
|
+
|
|
915
|
+
const listJson = await listResp.json().catch(() => ({}))
|
|
916
|
+
if (!listResp.ok) {
|
|
917
|
+
throw new Error(`PayPal webhook list failed (${listResp.status}): ${JSON.stringify(listJson)}`)
|
|
918
|
+
}
|
|
919
|
+
|
|
920
|
+
const existing = Array.isArray(listJson?.webhooks)
|
|
921
|
+
? listJson.webhooks.find((hook: any) => hook?.url === webhookUrl)
|
|
922
|
+
: null
|
|
923
|
+
|
|
924
|
+
let webhookId = existing?.id ? String(existing.id) : ""
|
|
925
|
+
|
|
926
|
+
if (!webhookId) {
|
|
927
|
+
const createResp = await fetch(`${baseUrl}/v1/notifications/webhooks`, {
|
|
928
|
+
method: "POST",
|
|
929
|
+
headers: {
|
|
930
|
+
Authorization: `Bearer ${accessToken}`,
|
|
931
|
+
"Content-Type": "application/json",
|
|
932
|
+
"PayPal-Partner-Attribution-Id": this.bnCode,
|
|
933
|
+
},
|
|
934
|
+
body: JSON.stringify({
|
|
935
|
+
url: webhookUrl,
|
|
936
|
+
event_types: [
|
|
937
|
+
{ name: "CHECKOUT.ORDER.APPROVED" },
|
|
938
|
+
{ name: "CHECKOUT.ORDER.CANCELLED" },
|
|
939
|
+
{ name: "PAYMENT.CAPTURE.COMPLETED" },
|
|
940
|
+
{ name: "PAYMENT.CAPTURE.DENIED" },
|
|
941
|
+
{ name: "PAYMENT.CAPTURE.REFUNDED" },
|
|
942
|
+
{ name: "PAYMENT.CAPTURE.REVERSED" },
|
|
943
|
+
{ name: "PAYMENT.AUTHORIZATION.CREATED" },
|
|
944
|
+
{ name: "PAYMENT.AUTHORIZATION.VOIDED" },
|
|
945
|
+
{ name: "PAYMENT.AUTHORIZATION.DENIED" },
|
|
946
|
+
{ name: "PAYMENT.REFUND.COMPLETED" },
|
|
947
|
+
{ name: "PAYMENT.REFUND.DENIED" },
|
|
948
|
+
],
|
|
949
|
+
}),
|
|
950
|
+
})
|
|
951
|
+
|
|
952
|
+
const createJson = await createResp.json().catch(() => ({}))
|
|
953
|
+
if (!createResp.ok) {
|
|
954
|
+
throw new Error(
|
|
955
|
+
`PayPal webhook create failed (${createResp.status}): ${JSON.stringify(createJson)}`
|
|
956
|
+
)
|
|
957
|
+
}
|
|
958
|
+
|
|
959
|
+
webhookId = String(createJson?.id || "")
|
|
960
|
+
}
|
|
961
|
+
|
|
962
|
+
if (!webhookId) {
|
|
963
|
+
throw new Error("PayPal webhook registration did not return an id")
|
|
964
|
+
}
|
|
965
|
+
|
|
966
|
+
const nextWebhookIds = { ...webhookIds, [env]: webhookId }
|
|
967
|
+
await this.saveSettings({
|
|
968
|
+
api_details: {
|
|
969
|
+
...apiDetails,
|
|
970
|
+
webhook_ids: nextWebhookIds,
|
|
971
|
+
},
|
|
972
|
+
})
|
|
973
|
+
|
|
974
|
+
await this.recordAuditEvent("webhook_registered", {
|
|
975
|
+
environment: env,
|
|
976
|
+
webhook_id: webhookId,
|
|
977
|
+
webhook_url: webhookUrl,
|
|
978
|
+
})
|
|
979
|
+
|
|
980
|
+
return webhookId
|
|
981
|
+
}
|
|
982
|
+
|
|
983
|
+
private maskValue(value?: string | null, visibleChars = 4) {
|
|
984
|
+
if (!value) return null
|
|
985
|
+
const trimmed = String(value)
|
|
986
|
+
if (trimmed.length <= visibleChars) {
|
|
987
|
+
return "•".repeat(trimmed.length)
|
|
988
|
+
}
|
|
989
|
+
return `${"•".repeat(Math.max(0, trimmed.length - visibleChars))}${trimmed.slice(
|
|
990
|
+
-visibleChars
|
|
991
|
+
)}`
|
|
992
|
+
}
|
|
993
|
+
|
|
994
|
+
async getStatus(envOverride?: Environment) {
|
|
995
|
+
const row = await this.getCurrentRow()
|
|
996
|
+
const env = envOverride ?? (await this.getCurrentEnvironment())
|
|
997
|
+
|
|
998
|
+
if (!row) {
|
|
999
|
+
return { environment: env, status: "disconnected" as Status, seller_client_id_present: false }
|
|
1000
|
+
}
|
|
1001
|
+
|
|
1002
|
+
const c = this.getEnvCreds(row, env)
|
|
1003
|
+
const hasCreds = !!(c.clientId && c.clientSecret)
|
|
1004
|
+
let sellerEmail: string | null = c.sellerEmail || row.seller_email || null
|
|
1005
|
+
let sellerMerchantId: string | null = c.sellerMerchantId || row.seller_merchant_id || null
|
|
1006
|
+
|
|
1007
|
+
if (!sellerEmail && hasCreds) {
|
|
1008
|
+
try {
|
|
1009
|
+
const hydrated = await this.fetchSellerProfileFromDirectCredentials(env)
|
|
1010
|
+
if (hydrated.sellerEmail || hydrated.sellerMerchantId) {
|
|
1011
|
+
await this.saveSellerCredentials({
|
|
1012
|
+
clientId: c.clientId!,
|
|
1013
|
+
clientSecret: c.clientSecret!,
|
|
1014
|
+
sellerMerchantId: hydrated.sellerMerchantId || sellerMerchantId,
|
|
1015
|
+
sellerEmail: hydrated.sellerEmail || sellerEmail,
|
|
1016
|
+
environment: env,
|
|
1017
|
+
})
|
|
1018
|
+
|
|
1019
|
+
const refreshedRow = await this.getCurrentRow()
|
|
1020
|
+
if (refreshedRow) {
|
|
1021
|
+
const refreshedCreds = this.getEnvCreds(refreshedRow, env)
|
|
1022
|
+
sellerEmail = refreshedCreds.sellerEmail || refreshedRow.seller_email || sellerEmail
|
|
1023
|
+
sellerMerchantId =
|
|
1024
|
+
refreshedCreds.sellerMerchantId || refreshedRow.seller_merchant_id || sellerMerchantId
|
|
1025
|
+
}
|
|
1026
|
+
}
|
|
1027
|
+
} catch (e: any) {
|
|
1028
|
+
console.warn("[PayPal] status direct credential lookup failed:", e?.message || e)
|
|
1029
|
+
}
|
|
1030
|
+
}
|
|
1031
|
+
|
|
1032
|
+
return {
|
|
1033
|
+
environment: env,
|
|
1034
|
+
status: (hasCreds ? "connected" : "disconnected") as Status,
|
|
1035
|
+
shared_id: row.shared_id ?? null,
|
|
1036
|
+
auth_code: row.auth_code ? "***stored***" : null,
|
|
1037
|
+
seller_client_id_present: hasCreds,
|
|
1038
|
+
seller_client_id_masked: this.maskValue(c.clientId),
|
|
1039
|
+
seller_client_secret_masked: c.clientSecret ? "••••••••" : null,
|
|
1040
|
+
seller_merchant_id: sellerMerchantId,
|
|
1041
|
+
seller_email: sellerEmail,
|
|
1042
|
+
updated_at: (row.updated_at as any)?.toISOString?.() ?? null,
|
|
1043
|
+
}
|
|
1044
|
+
}
|
|
1045
|
+
|
|
1046
|
+
async disconnect() {
|
|
1047
|
+
const row = await this.getCurrentRow()
|
|
1048
|
+
if (!row) return
|
|
1049
|
+
const env = await this.getCurrentEnvironment()
|
|
1050
|
+
|
|
1051
|
+
const meta = (row.metadata || {}) as any
|
|
1052
|
+
const creds = { ...(meta.credentials || {}) }
|
|
1053
|
+
delete creds[env]
|
|
1054
|
+
|
|
1055
|
+
const hasAnyCreds = Object.values(creds).some((v: any) => {
|
|
1056
|
+
return v && typeof v === "object" && (v as any).client_id && (v as any).client_secret
|
|
1057
|
+
})
|
|
1058
|
+
|
|
1059
|
+
await this.updatePayPalConnections({
|
|
1060
|
+
id: row.id,
|
|
1061
|
+
status: hasAnyCreds ? "connected" : "disconnected",
|
|
1062
|
+
shared_id: null,
|
|
1063
|
+
auth_code: null,
|
|
1064
|
+
seller_client_id: null,
|
|
1065
|
+
seller_client_secret: null,
|
|
1066
|
+
seller_merchant_id: null,
|
|
1067
|
+
seller_email: null,
|
|
1068
|
+
app_access_token: null,
|
|
1069
|
+
app_access_token_expires_at: null,
|
|
1070
|
+
metadata: {
|
|
1071
|
+
...(row.metadata || {}),
|
|
1072
|
+
credentials: creds,
|
|
1073
|
+
active_environment: env,
|
|
1074
|
+
},
|
|
1075
|
+
})
|
|
1076
|
+
const updated = await this.getCurrentRow()
|
|
1077
|
+
if (updated) {
|
|
1078
|
+
await this.syncRowFieldsFromMetadata(updated, env)
|
|
1079
|
+
}
|
|
1080
|
+
await this.recordAuditEvent("disconnected", { environment: env })
|
|
1081
|
+
}
|
|
1082
|
+
|
|
1083
|
+
async getAppAccessToken(): Promise<string> {
|
|
1084
|
+
const row = await this.getCurrentRow()
|
|
1085
|
+
const env = await this.getCurrentEnvironment()
|
|
1086
|
+
const creds = await this.getActiveCredentials()
|
|
1087
|
+
|
|
1088
|
+
if (!row) {
|
|
1089
|
+
throw new Error("PayPal connection row not found. Please complete onboarding.")
|
|
1090
|
+
}
|
|
1091
|
+
|
|
1092
|
+
const expiresAt = row.app_access_token_expires_at ? new Date(row.app_access_token_expires_at as any) : null
|
|
1093
|
+
if (row.app_access_token && expiresAt) {
|
|
1094
|
+
const msLeft = expiresAt.getTime() - Date.now()
|
|
1095
|
+
if (msLeft > 2 * 60 * 1000) return row.app_access_token
|
|
1096
|
+
}
|
|
1097
|
+
|
|
1098
|
+
const baseUrl = env === "live" ? "https://api-m.paypal.com" : "https://api-m.sandbox.paypal.com"
|
|
1099
|
+
const basic = Buffer.from(`${creds.client_id}:${creds.client_secret}`).toString("base64")
|
|
1100
|
+
|
|
1101
|
+
const body = new URLSearchParams()
|
|
1102
|
+
body.set("grant_type", "client_credentials")
|
|
1103
|
+
|
|
1104
|
+
const res = await fetch(`${baseUrl}/v1/oauth2/token`, {
|
|
1105
|
+
method: "POST",
|
|
1106
|
+
headers: {
|
|
1107
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
1108
|
+
Authorization: `Basic ${basic}`,
|
|
1109
|
+
"PayPal-Partner-Attribution-Id": this.bnCode,
|
|
1110
|
+
},
|
|
1111
|
+
body,
|
|
1112
|
+
})
|
|
1113
|
+
|
|
1114
|
+
const json = await res.json().catch(() => ({}))
|
|
1115
|
+
if (!res.ok) throw new Error(`PayPal client_credentials failed (${res.status}): ${JSON.stringify(json)}`)
|
|
1116
|
+
|
|
1117
|
+
const accessToken = String(json.access_token)
|
|
1118
|
+
const expiresIn = Number(json.expires_in || 3600)
|
|
1119
|
+
const newExpiresAt = new Date(Date.now() + expiresIn * 1000)
|
|
1120
|
+
|
|
1121
|
+
await this.updatePayPalConnections({
|
|
1122
|
+
id: row.id,
|
|
1123
|
+
app_access_token: accessToken,
|
|
1124
|
+
app_access_token_expires_at: newExpiresAt as any,
|
|
1125
|
+
})
|
|
1126
|
+
|
|
1127
|
+
return accessToken
|
|
1128
|
+
}
|
|
1129
|
+
|
|
1130
|
+
async generateClientToken(opts?: { locale?: string }): Promise<string> {
|
|
1131
|
+
const env = await this.getCurrentEnvironment()
|
|
1132
|
+
const baseUrl = env === "live" ? "https://api-m.paypal.com" : "https://api-m.sandbox.paypal.com"
|
|
1133
|
+
|
|
1134
|
+
const accessToken = await this.getAppAccessToken()
|
|
1135
|
+
|
|
1136
|
+
const res = await fetch(`${baseUrl}/v1/identity/generate-token`, {
|
|
1137
|
+
method: "POST",
|
|
1138
|
+
headers: {
|
|
1139
|
+
Authorization: `Bearer ${accessToken}`,
|
|
1140
|
+
"Content-Type": "application/json",
|
|
1141
|
+
Accept: "application/json",
|
|
1142
|
+
"PayPal-Partner-Attribution-Id": this.bnCode,
|
|
1143
|
+
...(opts?.locale ? { "Accept-Language": opts.locale } : {}),
|
|
1144
|
+
},
|
|
1145
|
+
})
|
|
1146
|
+
|
|
1147
|
+
const json = await res.json().catch(() => ({}))
|
|
1148
|
+
if (!res.ok) {
|
|
1149
|
+
throw new Error(`PayPal generate-token failed (${res.status}): ${JSON.stringify(json)}`)
|
|
1150
|
+
}
|
|
1151
|
+
|
|
1152
|
+
const token = String((json as any)?.client_token || "")
|
|
1153
|
+
if (!token) {
|
|
1154
|
+
throw new Error("PayPal client_token is missing in generate-token response")
|
|
1155
|
+
}
|
|
1156
|
+
|
|
1157
|
+
return token
|
|
1158
|
+
}
|
|
1159
|
+
|
|
1160
|
+
async getSettings() {
|
|
1161
|
+
const rows = await this.listPayPalSettings({})
|
|
1162
|
+
const row = rows?.[0]
|
|
1163
|
+
return { data: (row?.data || {}) as Record<string, any> }
|
|
1164
|
+
}
|
|
1165
|
+
|
|
1166
|
+
private deepMerge(
|
|
1167
|
+
target: Record<string, any>,
|
|
1168
|
+
source: Record<string, any>
|
|
1169
|
+
): Record<string, any> {
|
|
1170
|
+
const result = { ...target }
|
|
1171
|
+
for (const key of Object.keys(source)) {
|
|
1172
|
+
const sv = source[key]
|
|
1173
|
+
const tv = target[key]
|
|
1174
|
+
if (
|
|
1175
|
+
sv !== null &&
|
|
1176
|
+
typeof sv === "object" &&
|
|
1177
|
+
!Array.isArray(sv) &&
|
|
1178
|
+
tv !== null &&
|
|
1179
|
+
typeof tv === "object" &&
|
|
1180
|
+
!Array.isArray(tv)
|
|
1181
|
+
) {
|
|
1182
|
+
result[key] = this.deepMerge(tv, sv)
|
|
1183
|
+
} else {
|
|
1184
|
+
result[key] = sv
|
|
1185
|
+
}
|
|
1186
|
+
}
|
|
1187
|
+
return result
|
|
1188
|
+
}
|
|
1189
|
+
|
|
1190
|
+
async saveSettings(patch: Record<string, any>) {
|
|
1191
|
+
const rows = await this.listPayPalSettings({})
|
|
1192
|
+
const row = rows?.[0]
|
|
1193
|
+
const current = (row?.data || {}) as Record<string, any>
|
|
1194
|
+
|
|
1195
|
+
const next = this.deepMerge(current, patch)
|
|
1196
|
+
|
|
1197
|
+
if (!row) {
|
|
1198
|
+
const created = await this.createPayPalSettings({ data: next })
|
|
1199
|
+
return { data: (created.data || {}) as Record<string, any> }
|
|
1200
|
+
}
|
|
1201
|
+
|
|
1202
|
+
await this.updatePayPalSettings({ id: row.id, data: next })
|
|
1203
|
+
return { data: next }
|
|
1204
|
+
}
|
|
1205
|
+
|
|
1206
|
+
async getActiveCredentials() {
|
|
1207
|
+
const row = await this.getCurrentRow()
|
|
1208
|
+
const env = await this.getCurrentEnvironment()
|
|
1209
|
+
|
|
1210
|
+
if (!row) {
|
|
1211
|
+
throw new Error("PayPal connection row not found. Please complete onboarding.")
|
|
1212
|
+
}
|
|
1213
|
+
|
|
1214
|
+
const c = this.getEnvCreds(row, env)
|
|
1215
|
+
const clientSecret = c.clientSecret || ""
|
|
1216
|
+
|
|
1217
|
+
if (!c.clientId || !clientSecret) {
|
|
1218
|
+
throw new Error(
|
|
1219
|
+
`PayPal credentials missing for environment "${env}". Please save credentials.`
|
|
1220
|
+
)
|
|
1221
|
+
}
|
|
1222
|
+
|
|
1223
|
+
return {
|
|
1224
|
+
environment: env,
|
|
1225
|
+
client_id: c.clientId,
|
|
1226
|
+
client_secret: clientSecret,
|
|
1227
|
+
}
|
|
1228
|
+
}
|
|
1229
|
+
|
|
1230
|
+
async getOrderDetails(orderId: string) {
|
|
1231
|
+
if (!orderId) {
|
|
1232
|
+
throw new Error("PayPal orderId is required")
|
|
1233
|
+
}
|
|
1234
|
+
|
|
1235
|
+
const creds = await this.getActiveCredentials()
|
|
1236
|
+
const base =
|
|
1237
|
+
creds.environment === "live"
|
|
1238
|
+
? "https://api-m.paypal.com"
|
|
1239
|
+
: "https://api-m.sandbox.paypal.com"
|
|
1240
|
+
const auth = Buffer.from(`${creds.client_id}:${creds.client_secret}`).toString("base64")
|
|
1241
|
+
|
|
1242
|
+
const tokenResp = await fetch(`${base}/v1/oauth2/token`, {
|
|
1243
|
+
method: "POST",
|
|
1244
|
+
headers: {
|
|
1245
|
+
Authorization: `Basic ${auth}`,
|
|
1246
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
1247
|
+
"PayPal-Partner-Attribution-Id": this.bnCode,
|
|
1248
|
+
},
|
|
1249
|
+
body: "grant_type=client_credentials",
|
|
1250
|
+
})
|
|
1251
|
+
|
|
1252
|
+
const tokenText = await tokenResp.text()
|
|
1253
|
+
if (!tokenResp.ok) {
|
|
1254
|
+
throw new Error(`PayPal token error (${tokenResp.status}): ${tokenText}`)
|
|
1255
|
+
}
|
|
1256
|
+
|
|
1257
|
+
const tokenJson = JSON.parse(tokenText)
|
|
1258
|
+
const accessToken = String(tokenJson.access_token)
|
|
1259
|
+
|
|
1260
|
+
const resp = await fetch(`${base}/v2/checkout/orders/${orderId}`, {
|
|
1261
|
+
method: "GET",
|
|
1262
|
+
headers: {
|
|
1263
|
+
Authorization: `Bearer ${accessToken}`,
|
|
1264
|
+
"Content-Type": "application/json",
|
|
1265
|
+
"PayPal-Partner-Attribution-Id": this.bnCode,
|
|
1266
|
+
},
|
|
1267
|
+
})
|
|
1268
|
+
|
|
1269
|
+
const text = await resp.text()
|
|
1270
|
+
if (!resp.ok) {
|
|
1271
|
+
throw new Error(`PayPal get order error (${resp.status}): ${text}`)
|
|
1272
|
+
}
|
|
1273
|
+
|
|
1274
|
+
return JSON.parse(text)
|
|
1275
|
+
}
|
|
1276
|
+
|
|
1277
|
+
async createWebhookEventRecord(input: {
|
|
1278
|
+
event_id: string
|
|
1279
|
+
event_type: string
|
|
1280
|
+
resource_id?: string | null
|
|
1281
|
+
payload?: Record<string, unknown>
|
|
1282
|
+
event_version?: string | null
|
|
1283
|
+
transmission_id?: string | null
|
|
1284
|
+
transmission_time?: Date | null
|
|
1285
|
+
status?: string
|
|
1286
|
+
attempt_count?: number
|
|
1287
|
+
}) {
|
|
1288
|
+
try {
|
|
1289
|
+
const created = await this.createPayPalWebhookEvents({
|
|
1290
|
+
event_id: input.event_id,
|
|
1291
|
+
event_type: input.event_type,
|
|
1292
|
+
resource_id: input.resource_id ?? null,
|
|
1293
|
+
payload: input.payload ?? {},
|
|
1294
|
+
event_version: input.event_version ?? null,
|
|
1295
|
+
transmission_id: input.transmission_id ?? null,
|
|
1296
|
+
transmission_time: input.transmission_time ?? null,
|
|
1297
|
+
status: input.status ?? "pending",
|
|
1298
|
+
attempt_count: input.attempt_count ?? 0,
|
|
1299
|
+
next_retry_at: null,
|
|
1300
|
+
processed_at: null,
|
|
1301
|
+
last_error: null,
|
|
1302
|
+
})
|
|
1303
|
+
return { created: true, event: created }
|
|
1304
|
+
} catch (error: any) {
|
|
1305
|
+
const message = String(error?.message || "")
|
|
1306
|
+
if (message.includes("paypal_webhook_event_event_id_unique") || message.includes("unique")) {
|
|
1307
|
+
const existing = await this.listPayPalWebhookEvents({ event_id: input.event_id })
|
|
1308
|
+
return { created: false, event: existing?.[0] ?? null }
|
|
1309
|
+
}
|
|
1310
|
+
throw error
|
|
1311
|
+
}
|
|
1312
|
+
}
|
|
1313
|
+
|
|
1314
|
+
async updateWebhookEventRecord(input: {
|
|
1315
|
+
id: string
|
|
1316
|
+
status?: string
|
|
1317
|
+
attempt_count?: number
|
|
1318
|
+
next_retry_at?: Date | null
|
|
1319
|
+
processed_at?: Date | null
|
|
1320
|
+
last_error?: string | null
|
|
1321
|
+
resource_id?: string | null
|
|
1322
|
+
}) {
|
|
1323
|
+
return await this.updatePayPalWebhookEvents({
|
|
1324
|
+
id: input.id,
|
|
1325
|
+
status: input.status,
|
|
1326
|
+
attempt_count: input.attempt_count,
|
|
1327
|
+
next_retry_at: input.next_retry_at ?? null,
|
|
1328
|
+
processed_at: input.processed_at ?? null,
|
|
1329
|
+
last_error: input.last_error ?? null,
|
|
1330
|
+
resource_id: input.resource_id ?? null,
|
|
1331
|
+
})
|
|
1332
|
+
}
|
|
1333
|
+
|
|
1334
|
+
async recordAuditEvent(_eventType: string, _metadata?: Record<string, unknown>) {
|
|
1335
|
+
return null
|
|
1336
|
+
}
|
|
1337
|
+
|
|
1338
|
+
async recordMetric(name: string, metadata?: Record<string, unknown>) {
|
|
1339
|
+
const existing = await this.listPayPalMetrics({ name })
|
|
1340
|
+
const row = existing?.[0]
|
|
1341
|
+
const current = (row?.data || {}) as Record<string, any>
|
|
1342
|
+
const next = {
|
|
1343
|
+
...current,
|
|
1344
|
+
...(metadata || {}),
|
|
1345
|
+
count: Number(current.count || 0) + 1,
|
|
1346
|
+
last_recorded_at: new Date().toISOString(),
|
|
1347
|
+
}
|
|
1348
|
+
|
|
1349
|
+
if (!row) {
|
|
1350
|
+
return await this.createPayPalMetrics({
|
|
1351
|
+
name,
|
|
1352
|
+
data: next,
|
|
1353
|
+
})
|
|
1354
|
+
}
|
|
1355
|
+
|
|
1356
|
+
return await this.updatePayPalMetrics({
|
|
1357
|
+
id: row.id,
|
|
1358
|
+
name,
|
|
1359
|
+
data: next,
|
|
1360
|
+
})
|
|
1361
|
+
}
|
|
1362
|
+
|
|
1363
|
+
async recordPaymentLog(eventType: string, metadata?: Record<string, unknown>) {
|
|
1364
|
+
return await this.recordAuditEvent(`payment_${eventType}`, metadata)
|
|
1365
|
+
}
|
|
1366
|
+
|
|
1367
|
+
async sendAlert(input: {
|
|
1368
|
+
type: string
|
|
1369
|
+
message: string
|
|
1370
|
+
metadata?: Record<string, unknown>
|
|
1371
|
+
}) {
|
|
1372
|
+
const urls = this.getAlertWebhookUrls()
|
|
1373
|
+
if (urls.length === 0) {
|
|
1374
|
+
return
|
|
1375
|
+
}
|
|
1376
|
+
|
|
1377
|
+
const payload = {
|
|
1378
|
+
type: input.type,
|
|
1379
|
+
message: input.message,
|
|
1380
|
+
metadata: input.metadata ?? {},
|
|
1381
|
+
source: "paypal",
|
|
1382
|
+
timestamp: new Date().toISOString(),
|
|
1383
|
+
}
|
|
1384
|
+
|
|
1385
|
+
await Promise.all(
|
|
1386
|
+
urls.map(async (url) => {
|
|
1387
|
+
try {
|
|
1388
|
+
const resp = await fetch(url, {
|
|
1389
|
+
method: "POST",
|
|
1390
|
+
headers: {
|
|
1391
|
+
"Content-Type": "application/json",
|
|
1392
|
+
},
|
|
1393
|
+
body: JSON.stringify(payload),
|
|
1394
|
+
})
|
|
1395
|
+
if (!resp.ok) {
|
|
1396
|
+
const text = await resp.text().catch(() => "")
|
|
1397
|
+
await this.recordAuditEvent("alert_failed", {
|
|
1398
|
+
url,
|
|
1399
|
+
status: resp.status,
|
|
1400
|
+
response: text,
|
|
1401
|
+
})
|
|
1402
|
+
} else {
|
|
1403
|
+
await this.recordAuditEvent("alert_sent", { url, type: input.type })
|
|
1404
|
+
}
|
|
1405
|
+
} catch (error: any) {
|
|
1406
|
+
await this.recordAuditEvent("alert_failed", {
|
|
1407
|
+
url,
|
|
1408
|
+
message: error?.message,
|
|
1409
|
+
})
|
|
1410
|
+
}
|
|
1411
|
+
})
|
|
1412
|
+
)
|
|
1413
|
+
}
|
|
1414
|
+
}
|
|
1415
|
+
|
|
1416
|
+
export default PayPalModuleService
|