@bayarkit/paypal 0.0.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +93 -1
- package/dist/index.d.ts +97 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +433 -0
- package/dist/index.js.map +1 -0
- package/package.json +53 -2
package/README.md
CHANGED
|
@@ -1,3 +1,95 @@
|
|
|
1
1
|
# @bayarkit/paypal
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
PayPal Orders v2 (approve, then capture) for [bayarkit](https://github.com/sasateguh/bayarkit): one interface for Malaysian and global payment gateways, and a checked way to confirm a
|
|
4
|
+
payment. [Bahasa Melayu](#bahasa-melayu)
|
|
5
|
+
|
|
6
|
+
**Evidence:** Measured on the sandbox, 30 Oct 2026: buyer approval, captures in USD and MYR, real webhooks verified by PayPal's postback, full and partial refunds; paid only when the capture is `COMPLETED`.
|
|
7
|
+
|
|
8
|
+
```bash
|
|
9
|
+
npm install @bayarkit/core @bayarkit/paypal
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
```ts
|
|
13
|
+
import { checkoutResponse, confirmPayment, readCallback } from '@bayarkit/core'
|
|
14
|
+
import { paypal } from '@bayarkit/paypal'
|
|
15
|
+
|
|
16
|
+
const gateway = paypal({ clientId, clientSecret, webhookId, currency: 'USD' })
|
|
17
|
+
|
|
18
|
+
// Checkout: one reference per attempt; amounts are integers in the currency's smallest unit (USD 10.50 = 1050).
|
|
19
|
+
const payment = await gateway.createPayment({ reference, amount, description, customer, returnUrl, callbackUrl })
|
|
20
|
+
return checkoutResponse(payment) // a 303 redirect, or a self-submitting form when the gateway wants a POST
|
|
21
|
+
|
|
22
|
+
// Return route: the buyer approved on PayPal, and nothing has moved yet. Capture it (safe to retry)
|
|
23
|
+
// with the payment.id you saved at checkout (PayPal appends ?token=<order id>&PayerID=…; do not trust the query).
|
|
24
|
+
await gateway.capturePayment(order.paymentId)
|
|
25
|
+
|
|
26
|
+
// Callback route: never mark an order paid from the callback body or the return URL alone.
|
|
27
|
+
const result = await confirmPayment(gateway, await readCallback(request), { reference, amount, currency: 'USD' })
|
|
28
|
+
if (result.ok) {
|
|
29
|
+
// mark the order paid (idempotently: gateways retry callbacks)
|
|
30
|
+
}
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
PayPal takes two steps. The buyer approves on PayPal (order `APPROVED`: not paid), then your return route, or a `CHECKOUT.ORDER.APPROVED` webhook, calls `capturePayment(orderId)`. `getPayment` only reads and never captures. Mark the order paid with `confirmPayment` on the `PAYMENT.CAPTURE.COMPLETED` webhook. PayPal has no per-order callback URL: add a webhook to your REST app and pass its id as `webhookId`.
|
|
34
|
+
|
|
35
|
+
Test without real money: `startFakePayPal` from `@bayarkit/testing` answers like this gateway (signed callbacks
|
|
36
|
+
included), and `npx bayarkit-fake paypal` runs it as a server for tests in other languages.
|
|
37
|
+
|
|
38
|
+
## Traps
|
|
39
|
+
|
|
40
|
+
- An order can be `COMPLETED` while its capture is `DECLINED` or `PENDING`, and `APPROVED` only means the buyer agreed. Count a payment as paid only when `purchase_units[0].payments.captures[0].status` is `COMPLETED`, and take the amount from that capture. (developer.paypal.com/sandbox-testing/card-testing; Orders v2 OpenAPI capture_status)
|
|
41
|
+
- Put your reference in `purchase_units[0].custom_id` (up to 255 characters, hidden from the buyer, copied onto each capture). `invoice_id` is shown to the buyer and must be unique per account by default, so a second order for the same reference is refused. (Orders v2 OpenAPI purchase_unit_request)
|
|
42
|
+
- MYR works only for a PayPal account based in Malaysia; elsewhere PayPal converts it with a fee. HUF, JPY and TWD take no decimals in PayPal (HUF and TWD do in ISO 4217). A currency the account does not hold leaves the payment pending until the merchant accepts it. (developer.paypal.com/reference/currency-codes)
|
|
43
|
+
- Verify webhooks by posting the headers, your webhook id and the raw body to `/v1/notifications/verify-webhook-signature` and requiring `verification_status: SUCCESS`. Send the body exactly as received: parsing and re-serialising it can fail verification. Events from the dashboard simulator cannot be verified this way; test with real sandbox events. (developer.paypal.com/api/rest/webhooks/rest)
|
|
44
|
+
- `PayPal-Request-Id` makes create, capture and refund safe to retry: PayPal keeps it 6 hours on orders and 45 days on refunds. Use a new value per call type (never reuse the create id for capture); without the header a retry captures or refunds again. (developer.paypal.com/reference/guidelines/idempotency; /api/rest/requests)
|
|
45
|
+
- With `payment_source.paypal.experience_context` the new order is `PAYER_ACTION_REQUIRED` and the buyer link is `rel: payer-action`; the older flow returns `CREATED` with `rel: approve`. An uncaptured order expires: the guide says 3 hours, the API reference 6 hours. (developer.paypal.com/whats-an-order; Orders v2 OpenAPI order.links)
|
|
46
|
+
- Refunds take the capture id, not the order id: `POST /v2/payments/captures/{id}/refund` with an empty body refunds what is left, with `amount` refunds part. Over-refunding fails with `REFUND_AMOUNT_EXCEEDED`. (Payments v2 OpenAPI captures.refund)
|
|
47
|
+
|
|
48
|
+
The full contract, with a source for every fact: [gateways/paypal/spec.md](https://github.com/sasateguh/bayarkit/blob/main/gateways/paypal/spec.md).
|
|
49
|
+
|
|
50
|
+
## Need help installing?
|
|
51
|
+
|
|
52
|
+
If you would rather not wire up a payment gateway yourself, Sasa Teguh, the studio that maintains bayarkit, can install it on your site or app and test it against the gateway sandbox before you go live.
|
|
53
|
+
|
|
54
|
+
[Contact page](https://sasateguh.com/contact) · Email: [admin@sasateguh.com](mailto:admin@sasateguh.com) · WhatsApp: [017-762 9379](https://wa.me/60177629379)
|
|
55
|
+
|
|
56
|
+
## Bahasa Melayu
|
|
57
|
+
|
|
58
|
+
PayPal Orders v2 (lulus, kemudian tangkap) untuk [bayarkit](https://github.com/sasateguh/bayarkit): satu antara muka untuk gateway pembayaran Malaysia dan global, dan cara bersemak untuk
|
|
59
|
+
mengesahkan bayaran.
|
|
60
|
+
|
|
61
|
+
**Bukti:** Diukur pada sandbox, 30 Okt 2026: kelulusan pembeli, tangkapan dalam USD dan MYR, webhook sebenar disahkan melalui postback PayPal, bayaran balik penuh dan separa; dibayar hanya bila tangkapan `COMPLETED`.
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
npm install @bayarkit/core @bayarkit/paypal
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Contoh kod di atas sama untuk semua gateway, kecuali langkah tangkap di laluan kembali (di bawah). Jumlah ialah integer dalam unit terkecil
|
|
68
|
+
mata wang (USD 10.50 = 1050), dan `currency` dinyatakan dalam setiap semakan. Jangan tanda pesanan dibayar daripada badan callback atau pautan kembali semata-mata;
|
|
69
|
+
`confirmPayment` menyemak tandatangan, bertanya gateway semula, dan memadankan rujukan, jumlah dan mata wang.
|
|
70
|
+
|
|
71
|
+
PayPal ada dua langkah. Pembeli meluluskan di PayPal (pesanan `APPROVED`: belum dibayar), kemudian laluan kembali anda, atau webhook `CHECKOUT.ORDER.APPROVED`, memanggil `capturePayment(orderId)`. `getPayment` hanya membaca dan tidak pernah menangkap. Tanda pesanan dibayar dengan `confirmPayment` pada webhook `PAYMENT.CAPTURE.COMPLETED`. PayPal tiada URL callback bagi setiap pesanan: tambah webhook pada aplikasi REST anda dan hantar idnya sebagai `webhookId`.
|
|
72
|
+
|
|
73
|
+
Uji tanpa wang sebenar: `startFakePayPal` daripada `@bayarkit/testing`, atau `npx bayarkit-fake paypal` sebagai
|
|
74
|
+
pelayan.
|
|
75
|
+
|
|
76
|
+
## Perangkap
|
|
77
|
+
|
|
78
|
+
- Pesanan boleh berstatus `COMPLETED` sedangkan tangkapannya `DECLINED` atau `PENDING`, dan `APPROVED` hanya bermaksud pembeli bersetuju. Kira bayaran sebagai dibayar hanya bila `purchase_units[0].payments.captures[0].status` ialah `COMPLETED`, dan ambil amaun daripada tangkapan itu. (developer.paypal.com/sandbox-testing/card-testing; Orders v2 OpenAPI capture_status)
|
|
79
|
+
- Letak rujukan anda dalam `purchase_units[0].custom_id` (hingga 255 aksara, tidak dilihat pembeli, disalin ke setiap tangkapan). `invoice_id` dipapar kepada pembeli dan mesti unik bagi setiap akaun secara lalai, jadi pesanan kedua untuk rujukan yang sama ditolak. (Orders v2 OpenAPI purchase_unit_request)
|
|
80
|
+
- MYR hanya berfungsi untuk akaun PayPal yang berpangkalan di Malaysia; di tempat lain PayPal menukarnya dengan caj. HUF, JPY dan TWD tiada perpuluhan dalam PayPal (HUF dan TWD ada perpuluhan dalam ISO 4217). Mata wang yang tidak dipegang akaun membiarkan bayaran tertunda sehingga peniaga menerimanya. (developer.paypal.com/reference/currency-codes)
|
|
81
|
+
- Sahkan webhook dengan menghantar pengepala, id webhook anda dan badan mentah ke `/v1/notifications/verify-webhook-signature`, dan wajibkan `verification_status: SUCCESS`. Hantar badan tepat seperti diterima: menghurai dan mensiri semula boleh menggagalkan pengesahan. Acara daripada simulator dashboard tidak boleh disahkan dengan cara ini; uji dengan acara sandbox sebenar. (developer.paypal.com/api/rest/webhooks/rest)
|
|
82
|
+
- `PayPal-Request-Id` menjadikan cipta, tangkap dan bayaran balik selamat diulang: PayPal menyimpannya 6 jam untuk pesanan dan 45 hari untuk bayaran balik. Guna nilai baharu bagi setiap jenis panggilan (jangan guna semula id cipta untuk tangkap); tanpa pengepala itu, ulangan menangkap atau memulangkan sekali lagi. (developer.paypal.com/reference/guidelines/idempotency; /api/rest/requests)
|
|
83
|
+
- Dengan `payment_source.paypal.experience_context`, pesanan baharu berstatus `PAYER_ACTION_REQUIRED` dan pautan pembeli ialah `rel: payer-action`; aliran lama memulangkan `CREATED` dengan `rel: approve`. Pesanan yang tidak ditangkap akan luput: panduan menyebut 3 jam, rujukan API 6 jam. (developer.paypal.com/whats-an-order; Orders v2 OpenAPI order.links)
|
|
84
|
+
- Bayaran balik mengambil id tangkapan, bukan id pesanan: `POST /v2/payments/captures/{id}/refund` dengan badan kosong memulangkan baki, dengan `amount` memulangkan sebahagian. Pulangan melebihi baki gagal dengan `REFUND_AMOUNT_EXCEEDED`. (Payments v2 OpenAPI captures.refund)
|
|
85
|
+
|
|
86
|
+
Kontrak penuh, dengan sumber bagi setiap fakta: [gateways/paypal/spec.md](https://github.com/sasateguh/bayarkit/blob/main/gateways/paypal/spec.md).
|
|
87
|
+
Dokumentasi penuh: [README.ms.md](https://github.com/sasateguh/bayarkit/blob/main/README.ms.md).
|
|
88
|
+
|
|
89
|
+
## Perlukan bantuan memasang?
|
|
90
|
+
|
|
91
|
+
Jika anda tidak mahu menyambung gateway pembayaran sendiri, Sasa Teguh, studio yang menyelenggara bayarkit, boleh memasangnya di laman atau aplikasi anda dan mengujinya pada sandbox gateway sebelum anda mula menerima bayaran.
|
|
92
|
+
|
|
93
|
+
[Halaman hubungi](https://sasateguh.com/ms/contact) · E-mel: [admin@sasateguh.com](mailto:admin@sasateguh.com) · WhatsApp: [017-762 9379](https://wa.me/60177629379)
|
|
94
|
+
|
|
95
|
+
MIT © Muhammad Aiman
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PayPal Orders v2 adapter: one-time orders (intent CAPTURE), capture, webhooks and refunds over raw REST (no PayPal
|
|
3
|
+
* SDK, zero runtime dependencies).
|
|
4
|
+
*
|
|
5
|
+
* Written from the official docs, read 29 October 2026 (`gateways/paypal/spec.md`, a source next to every fact):
|
|
6
|
+
* developer.paypal.com and the OpenAPI files in paypal/paypal-rest-api-specifications. Measured on the sandbox
|
|
7
|
+
* 30 October 2026 (spec "Measured"): a sandbox buyer approving on PayPal, captures in USD, MYR and JPY, real webhooks
|
|
8
|
+
* verified by the postback, full and partial refunds. The adapter needed no change; what the docs left open:
|
|
9
|
+
* - A capture in a currency the account does not hold (JPY) is `PENDING` with
|
|
10
|
+
* `RECEIVING_PREFERENCE_MANDATES_MANUAL_ACTION` and sends `PAYMENT.CAPTURE.PENDING`, so `confirmPayment` says
|
|
11
|
+
* `not_paid`. MYR captured `COMPLETED` on the sandbox business account used.
|
|
12
|
+
* - Only `PAYMENT.CAPTURE.*` arrives after a capture (about 15 s later); `CHECKOUT.ORDER.COMPLETED` never did. A
|
|
13
|
+
* `PAYMENT.CAPTURE.REFUNDED` event carries the refund, which names no order, so it verifies as `null`: send refund
|
|
14
|
+
* events elsewhere, not to `confirmPayment`.
|
|
15
|
+
* - Refunds were `COMPLETED` at once; the capture becomes `PARTIALLY_REFUNDED`, then `REFUNDED`.
|
|
16
|
+
* - A retried webhook is the same delivery (same body, transmission id, time and signature) and still verifies;
|
|
17
|
+
* so does an event several minutes old. Deduplicate on the event id.
|
|
18
|
+
*
|
|
19
|
+
* - Auth: OAuth 2.0 client credentials (`POST /v1/oauth2/token`, HTTP Basic). The token is cached until a minute
|
|
20
|
+
* before `expires_in` runs out and fetched again then, or once when PayPal answers 401 to a cached one. Neither the
|
|
21
|
+
* client secret nor a token ever appears in an error.
|
|
22
|
+
* - Hosts: `environment: 'sandbox'` (the default, so a forgotten setting never moves real money) or `'live'`.
|
|
23
|
+
* - Amounts are integers in PayPal's smallest unit both ways (sen for MYR, cents for USD; whole units for HUF, JPY and
|
|
24
|
+
* TWD, which PayPal takes without decimals even where ISO 4217 has two). The adapter converts to and from PayPal's
|
|
25
|
+
* decimal strings (`toPayPalValue`, `fromPayPalValue`); no API takes ringgit or decimals.
|
|
26
|
+
* - Currency: `currency` in the config (default USD) is `info.currency`; an order may name another of PayPal's 24.
|
|
27
|
+
* MYR works only for a Malaysian PayPal account; a currency the account does not hold can leave the capture
|
|
28
|
+
* `PENDING` until the merchant accepts it in PayPal.
|
|
29
|
+
* - The reference goes in `purchase_units[0].custom_id` (1–255, not shown to the buyer, copied onto each capture).
|
|
30
|
+
* `invoice_id` is not used: PayPal shows it to the buyer and refuses it twice per account by default.
|
|
31
|
+
* - `createPayment` sends `payment_source.paypal.experience_context` with `returnUrl` as both return and cancel URL,
|
|
32
|
+
* so the order is `PAYER_ACTION_REQUIRED` and the buyer link is `rel: payer-action` (`rel: approve` is accepted
|
|
33
|
+
* too). `callbackUrl` is not sent: PayPal has no per-order webhook; add a webhook to the REST app and pass its id as
|
|
34
|
+
* `webhookId`. `expiresAt` is not sent (PayPal has no field for it). `idempotencyKey` becomes `PayPal-Request-Id`.
|
|
35
|
+
* - Paying is two steps. The buyer approves on PayPal (order `APPROVED`: nothing has moved, NOT paid), then the app
|
|
36
|
+
* calls `capturePayment(orderId)` on the return URL or on the `CHECKOUT.ORDER.APPROVED` webhook. `getPayment` only
|
|
37
|
+
* reads and never captures. `paid` = the order's capture is `COMPLETED`; an order `COMPLETED` with a `DECLINED`
|
|
38
|
+
* capture is failed, a `PENDING` capture is pending. A refunded capture still reads as paid (as Stripe and Paddle
|
|
39
|
+
* do); the refund is the `RefundRecord`. The order is marked paid with `confirmPayment` on the
|
|
40
|
+
* `PAYMENT.CAPTURE.COMPLETED` webhook, which carries the order id in `supplementary_data.related_ids.order_id`.
|
|
41
|
+
* - `capturePayment` always sends a `PayPal-Request-Id` (`capture-<order id>` unless you pass one), so a retried
|
|
42
|
+
* capture returns the first result instead of capturing again.
|
|
43
|
+
* - Refunds take the capture id, read from the order: `POST /v2/payments/captures/{id}/refund` with the amount. What
|
|
44
|
+
* may still be refunded = the capture minus the order's refunds that are not `FAILED`/`CANCELLED`. `PENDING` =
|
|
45
|
+
* `pending`, `COMPLETED` = `succeeded`, `FAILED`/`CANCELLED` = `failed`.
|
|
46
|
+
*
|
|
47
|
+
* Webhook verification is PayPal's postback: the five `PAYPAL-*` headers, your `webhookId` and the event go to
|
|
48
|
+
* `POST /v1/notifications/verify-webhook-signature`, which answers `SUCCESS` or `FAILURE`. The docs ask for the event
|
|
49
|
+
* exactly as received, so the raw body is spliced into the request (after checking it parses as one JSON object),
|
|
50
|
+
* never re-serialised. A missing header or a body that is not a JSON object is refused without calling PayPal.
|
|
51
|
+
* `FAILURE` = `null`; a failed call to PayPal rejects, which `confirmPayment` reports as `gateway_error`, not as a
|
|
52
|
+
* forgery. Simulator ("mock") events cannot be verified this way (PayPal says so); use real sandbox events. The docs
|
|
53
|
+
* name no replay window: deduplicate on the event id (`raw.event_id`).
|
|
54
|
+
*/
|
|
55
|
+
import { type Gateway, type GatewayInfo, type PaymentRecord, type RefundInput, type RefundRecord, type VerifiedCallback } from '@bayarkit/core';
|
|
56
|
+
/** The 24 currencies PayPal lists (spec §7). MYR only for a Malaysian account. */
|
|
57
|
+
export declare const CURRENCIES: readonly string[];
|
|
58
|
+
/** No decimal places in PayPal, whatever ISO 4217 says (spec §7). */
|
|
59
|
+
export declare const ZERO_DECIMAL: readonly string[];
|
|
60
|
+
export interface PayPalConfig {
|
|
61
|
+
/** The REST app's client id (sandbox and live apps have different ones). */
|
|
62
|
+
clientId: string;
|
|
63
|
+
clientSecret: string;
|
|
64
|
+
/** The id PayPal gave the webhook you added to this app; every verification names it. */
|
|
65
|
+
webhookId: string;
|
|
66
|
+
/** Default `sandbox`. */
|
|
67
|
+
environment?: 'sandbox' | 'live';
|
|
68
|
+
/** The currency an order is charged in when it names none, and `info.currency`. Default `USD`. */
|
|
69
|
+
currency?: string;
|
|
70
|
+
/** Shown on the PayPal page instead of the account's business name (at most 127 characters). */
|
|
71
|
+
brandName?: string;
|
|
72
|
+
/** Default `NO_SHIPPING` (no address asked); `GET_FROM_FILE` asks for one. */
|
|
73
|
+
shippingPreference?: 'NO_SHIPPING' | 'GET_FROM_FILE';
|
|
74
|
+
/** Override the host (tests point this at the fake from `@bayarkit/testing`). */
|
|
75
|
+
baseUrl?: string;
|
|
76
|
+
fetch?: typeof fetch;
|
|
77
|
+
timeoutMs?: number;
|
|
78
|
+
}
|
|
79
|
+
export declare const info: GatewayInfo;
|
|
80
|
+
/** An integer in PayPal's smallest unit as the decimal string PayPal takes: `1050` USD → `"10.50"`, `500` JPY → `"500"`. */
|
|
81
|
+
export declare function toPayPalValue(minor: number, currency: string): string;
|
|
82
|
+
/** A PayPal decimal string back to the smallest unit; NaN for anything else (more decimals than the currency has). */
|
|
83
|
+
export declare function fromPayPalValue(value: unknown, currency: string): number;
|
|
84
|
+
export declare function paypal(config: PayPalConfig): Gateway<Promise<VerifiedCallback | null>> & {
|
|
85
|
+
readonly info: GatewayInfo & {
|
|
86
|
+
currency: string;
|
|
87
|
+
};
|
|
88
|
+
/**
|
|
89
|
+
* Capture an order the buyer approved. Idempotent: the same `idempotencyKey` (default `capture-<orderId>`) returns the
|
|
90
|
+
* first capture. The record is PayPal's answer; mark the order paid with `confirmPayment` on the webhook.
|
|
91
|
+
*/
|
|
92
|
+
capturePayment(orderId: string, opts?: {
|
|
93
|
+
idempotencyKey?: string;
|
|
94
|
+
}): Promise<PaymentRecord>;
|
|
95
|
+
refundPayment(input: RefundInput): Promise<RefundRecord>;
|
|
96
|
+
};
|
|
97
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqDG;AACH,OAAO,EAKL,KAAK,OAAO,EAEZ,KAAK,WAAW,EAChB,KAAK,aAAa,EAElB,KAAK,WAAW,EAChB,KAAK,YAAY,EAEjB,KAAK,gBAAgB,EACtB,MAAM,gBAAgB,CAAA;AAKvB,kFAAkF;AAClF,eAAO,MAAM,UAAU,EAAE,SAAS,MAAM,EACsE,CAAA;AAC9G,qEAAqE;AACrE,eAAO,MAAM,YAAY,EAAE,SAAS,MAAM,EAA0B,CAAA;AAcpE,MAAM,WAAW,YAAY;IAC3B,4EAA4E;IAC5E,QAAQ,EAAE,MAAM,CAAA;IAChB,YAAY,EAAE,MAAM,CAAA;IACpB,yFAAyF;IACzF,SAAS,EAAE,MAAM,CAAA;IACjB,yBAAyB;IACzB,WAAW,CAAC,EAAE,SAAS,GAAG,MAAM,CAAA;IAChC,kGAAkG;IAClG,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,gGAAgG;IAChG,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,8EAA8E;IAC9E,kBAAkB,CAAC,EAAE,aAAa,GAAG,eAAe,CAAA;IACpD,iFAAiF;IACjF,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,KAAK,CAAC,EAAE,OAAO,KAAK,CAAA;IACpB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,eAAO,MAAM,IAAI,EAAE,WAUlB,CAAA;AAID,4HAA4H;AAC5H,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAIrE;AAED,sHAAsH;AACtH,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAQxE;AAuDD,wBAAgB,MAAM,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,CAAC,GAAG;IACxF,QAAQ,CAAC,IAAI,EAAE,WAAW,GAAG;QAAE,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAA;IACjD;;;OAGG;IACH,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE;QAAE,cAAc,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,aAAa,CAAC,CAAA;IAC3F,aAAa,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,CAAC,CAAA;CACzD,CA2TA"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,433 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PayPal Orders v2 adapter: one-time orders (intent CAPTURE), capture, webhooks and refunds over raw REST (no PayPal
|
|
3
|
+
* SDK, zero runtime dependencies).
|
|
4
|
+
*
|
|
5
|
+
* Written from the official docs, read 29 October 2026 (`gateways/paypal/spec.md`, a source next to every fact):
|
|
6
|
+
* developer.paypal.com and the OpenAPI files in paypal/paypal-rest-api-specifications. Measured on the sandbox
|
|
7
|
+
* 30 October 2026 (spec "Measured"): a sandbox buyer approving on PayPal, captures in USD, MYR and JPY, real webhooks
|
|
8
|
+
* verified by the postback, full and partial refunds. The adapter needed no change; what the docs left open:
|
|
9
|
+
* - A capture in a currency the account does not hold (JPY) is `PENDING` with
|
|
10
|
+
* `RECEIVING_PREFERENCE_MANDATES_MANUAL_ACTION` and sends `PAYMENT.CAPTURE.PENDING`, so `confirmPayment` says
|
|
11
|
+
* `not_paid`. MYR captured `COMPLETED` on the sandbox business account used.
|
|
12
|
+
* - Only `PAYMENT.CAPTURE.*` arrives after a capture (about 15 s later); `CHECKOUT.ORDER.COMPLETED` never did. A
|
|
13
|
+
* `PAYMENT.CAPTURE.REFUNDED` event carries the refund, which names no order, so it verifies as `null`: send refund
|
|
14
|
+
* events elsewhere, not to `confirmPayment`.
|
|
15
|
+
* - Refunds were `COMPLETED` at once; the capture becomes `PARTIALLY_REFUNDED`, then `REFUNDED`.
|
|
16
|
+
* - A retried webhook is the same delivery (same body, transmission id, time and signature) and still verifies;
|
|
17
|
+
* so does an event several minutes old. Deduplicate on the event id.
|
|
18
|
+
*
|
|
19
|
+
* - Auth: OAuth 2.0 client credentials (`POST /v1/oauth2/token`, HTTP Basic). The token is cached until a minute
|
|
20
|
+
* before `expires_in` runs out and fetched again then, or once when PayPal answers 401 to a cached one. Neither the
|
|
21
|
+
* client secret nor a token ever appears in an error.
|
|
22
|
+
* - Hosts: `environment: 'sandbox'` (the default, so a forgotten setting never moves real money) or `'live'`.
|
|
23
|
+
* - Amounts are integers in PayPal's smallest unit both ways (sen for MYR, cents for USD; whole units for HUF, JPY and
|
|
24
|
+
* TWD, which PayPal takes without decimals even where ISO 4217 has two). The adapter converts to and from PayPal's
|
|
25
|
+
* decimal strings (`toPayPalValue`, `fromPayPalValue`); no API takes ringgit or decimals.
|
|
26
|
+
* - Currency: `currency` in the config (default USD) is `info.currency`; an order may name another of PayPal's 24.
|
|
27
|
+
* MYR works only for a Malaysian PayPal account; a currency the account does not hold can leave the capture
|
|
28
|
+
* `PENDING` until the merchant accepts it in PayPal.
|
|
29
|
+
* - The reference goes in `purchase_units[0].custom_id` (1–255, not shown to the buyer, copied onto each capture).
|
|
30
|
+
* `invoice_id` is not used: PayPal shows it to the buyer and refuses it twice per account by default.
|
|
31
|
+
* - `createPayment` sends `payment_source.paypal.experience_context` with `returnUrl` as both return and cancel URL,
|
|
32
|
+
* so the order is `PAYER_ACTION_REQUIRED` and the buyer link is `rel: payer-action` (`rel: approve` is accepted
|
|
33
|
+
* too). `callbackUrl` is not sent: PayPal has no per-order webhook; add a webhook to the REST app and pass its id as
|
|
34
|
+
* `webhookId`. `expiresAt` is not sent (PayPal has no field for it). `idempotencyKey` becomes `PayPal-Request-Id`.
|
|
35
|
+
* - Paying is two steps. The buyer approves on PayPal (order `APPROVED`: nothing has moved, NOT paid), then the app
|
|
36
|
+
* calls `capturePayment(orderId)` on the return URL or on the `CHECKOUT.ORDER.APPROVED` webhook. `getPayment` only
|
|
37
|
+
* reads and never captures. `paid` = the order's capture is `COMPLETED`; an order `COMPLETED` with a `DECLINED`
|
|
38
|
+
* capture is failed, a `PENDING` capture is pending. A refunded capture still reads as paid (as Stripe and Paddle
|
|
39
|
+
* do); the refund is the `RefundRecord`. The order is marked paid with `confirmPayment` on the
|
|
40
|
+
* `PAYMENT.CAPTURE.COMPLETED` webhook, which carries the order id in `supplementary_data.related_ids.order_id`.
|
|
41
|
+
* - `capturePayment` always sends a `PayPal-Request-Id` (`capture-<order id>` unless you pass one), so a retried
|
|
42
|
+
* capture returns the first result instead of capturing again.
|
|
43
|
+
* - Refunds take the capture id, read from the order: `POST /v2/payments/captures/{id}/refund` with the amount. What
|
|
44
|
+
* may still be refunded = the capture minus the order's refunds that are not `FAILED`/`CANCELLED`. `PENDING` =
|
|
45
|
+
* `pending`, `COMPLETED` = `succeeded`, `FAILED`/`CANCELLED` = `failed`.
|
|
46
|
+
*
|
|
47
|
+
* Webhook verification is PayPal's postback: the five `PAYPAL-*` headers, your `webhookId` and the event go to
|
|
48
|
+
* `POST /v1/notifications/verify-webhook-signature`, which answers `SUCCESS` or `FAILURE`. The docs ask for the event
|
|
49
|
+
* exactly as received, so the raw body is spliced into the request (after checking it parses as one JSON object),
|
|
50
|
+
* never re-serialised. A missing header or a body that is not a JSON object is refused without calling PayPal.
|
|
51
|
+
* `FAILURE` = `null`; a failed call to PayPal rejects, which `confirmPayment` reports as `gateway_error`, not as a
|
|
52
|
+
* forgery. Simulator ("mock") events cannot be verified this way (PayPal says so); use real sandbox events. The docs
|
|
53
|
+
* name no replay window: deduplicate on the event id (`raw.event_id`).
|
|
54
|
+
*/
|
|
55
|
+
import { checkRefundAmount, GatewayError, } from '@bayarkit/core';
|
|
56
|
+
const SANDBOX_HOST = 'api-m.sandbox.paypal.com';
|
|
57
|
+
const LIVE_HOST = 'api-m.paypal.com';
|
|
58
|
+
/** The 24 currencies PayPal lists (spec §7). MYR only for a Malaysian account. */
|
|
59
|
+
export const CURRENCIES = 'AUD BRL CAD CNY CZK DKK EUR HKD HUF ILS JPY MYR MXN TWD NZD NOK PHP PLN GBP SGD SEK CHF THB USD'.split(' ');
|
|
60
|
+
/** No decimal places in PayPal, whatever ISO 4217 says (spec §7). */
|
|
61
|
+
export const ZERO_DECIMAL = ['HUF', 'JPY', 'TWD'];
|
|
62
|
+
const REFERENCE_MAX = 255;
|
|
63
|
+
const REQUEST_ID_MAX = 108;
|
|
64
|
+
/** Refresh a cached token this long before PayPal says it expires. */
|
|
65
|
+
const TOKEN_MARGIN_MS = 60_000;
|
|
66
|
+
/** The webhook headers that go into the postback (spec §5). */
|
|
67
|
+
const HEADERS = {
|
|
68
|
+
auth_algo: 'paypal-auth-algo',
|
|
69
|
+
cert_url: 'paypal-cert-url',
|
|
70
|
+
transmission_id: 'paypal-transmission-id',
|
|
71
|
+
transmission_sig: 'paypal-transmission-sig',
|
|
72
|
+
transmission_time: 'paypal-transmission-time',
|
|
73
|
+
};
|
|
74
|
+
export const info = {
|
|
75
|
+
name: 'paypal',
|
|
76
|
+
minimumAmount: 1,
|
|
77
|
+
currency: 'USD',
|
|
78
|
+
evidence: {
|
|
79
|
+
kind: 'measured',
|
|
80
|
+
date: '2026-10-30',
|
|
81
|
+
source: 'PayPal sandbox: buyer approval, captures (USD, MYR, JPY pending), real webhooks, refunds; developer.paypal.com',
|
|
82
|
+
},
|
|
83
|
+
};
|
|
84
|
+
const decimalsOf = (currency) => (ZERO_DECIMAL.includes(currency) ? 0 : 2);
|
|
85
|
+
/** An integer in PayPal's smallest unit as the decimal string PayPal takes: `1050` USD → `"10.50"`, `500` JPY → `"500"`. */
|
|
86
|
+
export function toPayPalValue(minor, currency) {
|
|
87
|
+
if (!Number.isSafeInteger(minor) || minor < 0)
|
|
88
|
+
throw new RangeError('amount must be a whole number ≥ 0');
|
|
89
|
+
if (decimalsOf(currency) === 0)
|
|
90
|
+
return String(minor);
|
|
91
|
+
return `${Math.trunc(minor / 100)}.${String(minor % 100).padStart(2, '0')}`;
|
|
92
|
+
}
|
|
93
|
+
/** A PayPal decimal string back to the smallest unit; NaN for anything else (more decimals than the currency has). */
|
|
94
|
+
export function fromPayPalValue(value, currency) {
|
|
95
|
+
if (typeof value !== 'string')
|
|
96
|
+
return Number.NaN;
|
|
97
|
+
const m = /^(\d{1,15})(?:\.(\d+))?$/.exec(value);
|
|
98
|
+
if (!m)
|
|
99
|
+
return Number.NaN;
|
|
100
|
+
const decimals = decimalsOf(currency);
|
|
101
|
+
const frac = m[2] ?? '';
|
|
102
|
+
if (frac.length > decimals && !/^0*$/.test(frac.slice(decimals)))
|
|
103
|
+
return Number.NaN;
|
|
104
|
+
return Number(m[1]) * 10 ** decimals + Number(frac.slice(0, decimals).padEnd(decimals, '0') || '0');
|
|
105
|
+
}
|
|
106
|
+
const obj = (v) => (v && typeof v === 'object' && !Array.isArray(v) ? v : {});
|
|
107
|
+
const list = (v) => (Array.isArray(v) ? v.map(obj) : []);
|
|
108
|
+
const text = (v) => (typeof v === 'string' ? v : '');
|
|
109
|
+
/** PayPal order and capture ids are upper-case letters and digits; anything else never reaches a URL. */
|
|
110
|
+
const ID = /^[A-Z0-9]{1,64}$/;
|
|
111
|
+
/** Access tokens (`A21AA…`) and Basic credentials, should PayPal ever echo one. */
|
|
112
|
+
const SECRETISH = /\b(A21AA[A-Za-z0-9_-]+|Basic\s+[A-Za-z0-9+/=]+)/g;
|
|
113
|
+
const captureStatus = (s) => {
|
|
114
|
+
switch (s) {
|
|
115
|
+
case 'COMPLETED':
|
|
116
|
+
case 'PARTIALLY_REFUNDED':
|
|
117
|
+
case 'REFUNDED':
|
|
118
|
+
return 'paid';
|
|
119
|
+
case 'PENDING':
|
|
120
|
+
return 'pending';
|
|
121
|
+
case 'DECLINED':
|
|
122
|
+
case 'FAILED':
|
|
123
|
+
return 'failed';
|
|
124
|
+
default:
|
|
125
|
+
return 'unknown';
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
const orderStatus = (s) => {
|
|
129
|
+
switch (s) {
|
|
130
|
+
case 'CREATED':
|
|
131
|
+
case 'SAVED':
|
|
132
|
+
case 'PAYER_ACTION_REQUIRED':
|
|
133
|
+
case 'APPROVED': // the buyer said yes; nothing is captured
|
|
134
|
+
return 'pending';
|
|
135
|
+
case 'VOIDED':
|
|
136
|
+
return 'cancelled';
|
|
137
|
+
default:
|
|
138
|
+
return 'unknown';
|
|
139
|
+
}
|
|
140
|
+
};
|
|
141
|
+
const refundStatus = (s) => s === 'PENDING'
|
|
142
|
+
? 'pending'
|
|
143
|
+
: s === 'COMPLETED'
|
|
144
|
+
? 'succeeded'
|
|
145
|
+
: s === 'FAILED' || s === 'CANCELLED'
|
|
146
|
+
? 'failed'
|
|
147
|
+
: 'unknown';
|
|
148
|
+
const headerOf = (headers, name) => {
|
|
149
|
+
const v = headers[name] ?? Object.entries(headers).find(([k]) => k.toLowerCase() === name)?.[1];
|
|
150
|
+
return typeof v === 'string' ? v : '';
|
|
151
|
+
};
|
|
152
|
+
export function paypal(config) {
|
|
153
|
+
const fail = (message, code) => new GatewayError(message, 'paypal', 0, code);
|
|
154
|
+
if (!config.clientId || !config.clientSecret)
|
|
155
|
+
throw fail('clientId and clientSecret are required', 'CONFIG');
|
|
156
|
+
if (!config.webhookId)
|
|
157
|
+
throw fail('webhookId is required (the id of the webhook on your REST app)', 'CONFIG');
|
|
158
|
+
const environment = config.environment ?? 'sandbox';
|
|
159
|
+
if (environment !== 'sandbox' && environment !== 'live') {
|
|
160
|
+
throw fail("environment must be 'sandbox' or 'live'", 'CONFIG');
|
|
161
|
+
}
|
|
162
|
+
const base = (config.baseUrl ?? `https://${environment === 'live' ? LIVE_HOST : SANDBOX_HOST}`).replace(/\/+$/, '');
|
|
163
|
+
const doFetch = config.fetch ?? fetch;
|
|
164
|
+
const timeoutMs = config.timeoutMs ?? 15_000;
|
|
165
|
+
const currency = config.currency ?? 'USD';
|
|
166
|
+
if (!CURRENCIES.includes(currency)) {
|
|
167
|
+
throw fail(`PayPal does not take ${currency}; pick one of ${CURRENCIES.join(', ')}`, 'CURRENCY');
|
|
168
|
+
}
|
|
169
|
+
const gatewayInfo = { ...info, currency };
|
|
170
|
+
async function send(url, init) {
|
|
171
|
+
try {
|
|
172
|
+
return await doFetch(url, { ...init, signal: AbortSignal.timeout(timeoutMs) });
|
|
173
|
+
}
|
|
174
|
+
catch (e) {
|
|
175
|
+
throw new GatewayError(`unreachable (${e instanceof Error ? e.name : 'error'})`, 'paypal', 0);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
let token = null;
|
|
179
|
+
let pending = null;
|
|
180
|
+
async function fetchToken() {
|
|
181
|
+
const basic = Buffer.from(`${config.clientId}:${config.clientSecret}`).toString('base64');
|
|
182
|
+
const res = await send(`${base}/v1/oauth2/token`, {
|
|
183
|
+
method: 'POST',
|
|
184
|
+
headers: {
|
|
185
|
+
authorization: `Basic ${basic}`,
|
|
186
|
+
'content-type': 'application/x-www-form-urlencoded',
|
|
187
|
+
accept: 'application/json',
|
|
188
|
+
},
|
|
189
|
+
body: 'grant_type=client_credentials',
|
|
190
|
+
});
|
|
191
|
+
const json = obj(await res.json().catch(() => null));
|
|
192
|
+
const value = text(json.access_token);
|
|
193
|
+
const seconds = typeof json.expires_in === 'number' ? json.expires_in : 0;
|
|
194
|
+
if (!res.ok || !value) {
|
|
195
|
+
// OAuth's own words (`error`, `error_description`) only.
|
|
196
|
+
const words = [text(json.error), text(json.error_description)].filter(Boolean).join(': ');
|
|
197
|
+
throw new GatewayError(`token: ${(words || `HTTP ${res.status}`).replace(SECRETISH, '[redacted]')}`, 'paypal', res.status, text(json.error) || undefined);
|
|
198
|
+
}
|
|
199
|
+
token = { value, until: Date.now() + seconds * 1000 - TOKEN_MARGIN_MS };
|
|
200
|
+
return value;
|
|
201
|
+
}
|
|
202
|
+
async function bearer() {
|
|
203
|
+
if (token && token.until > Date.now())
|
|
204
|
+
return token.value;
|
|
205
|
+
// One token request at a time: concurrent calls wait for the same one.
|
|
206
|
+
pending ??= fetchToken().finally(() => {
|
|
207
|
+
pending = null;
|
|
208
|
+
});
|
|
209
|
+
return pending;
|
|
210
|
+
}
|
|
211
|
+
async function request(method, path, body, requestId) {
|
|
212
|
+
const call = async (value) => {
|
|
213
|
+
const headers = {
|
|
214
|
+
authorization: `Bearer ${value}`,
|
|
215
|
+
accept: 'application/json',
|
|
216
|
+
prefer: 'return=representation',
|
|
217
|
+
};
|
|
218
|
+
if (body !== undefined)
|
|
219
|
+
headers['content-type'] = 'application/json';
|
|
220
|
+
if (requestId)
|
|
221
|
+
headers['paypal-request-id'] = requestId;
|
|
222
|
+
return send(`${base}${path}`, {
|
|
223
|
+
method,
|
|
224
|
+
headers,
|
|
225
|
+
...(body !== undefined ? { body: typeof body === 'string' ? body : JSON.stringify(body) } : {}),
|
|
226
|
+
});
|
|
227
|
+
};
|
|
228
|
+
const used = await bearer();
|
|
229
|
+
let res = await call(used);
|
|
230
|
+
if (res.status === 401) {
|
|
231
|
+
// PayPal ended the token early: fetch a new one once.
|
|
232
|
+
if (token?.value === used)
|
|
233
|
+
token = null;
|
|
234
|
+
res = await call(await bearer());
|
|
235
|
+
}
|
|
236
|
+
const json = (await res.json().catch(() => null));
|
|
237
|
+
if (!res.ok || !json) {
|
|
238
|
+
// PayPal's own words only: `name`, `details[].issue`, `details[].description`, `debug_id`.
|
|
239
|
+
const e = obj(json);
|
|
240
|
+
const detail = list(e.details)[0] ?? {};
|
|
241
|
+
const words = [text(e.name), text(detail.issue), text(detail.description) || text(e.message)]
|
|
242
|
+
.filter(Boolean)
|
|
243
|
+
.join(': ') || `HTTP ${res.status}`;
|
|
244
|
+
const debug = text(e.debug_id);
|
|
245
|
+
throw new GatewayError(`${words}${debug ? ` (debug_id ${debug})` : ''}`.replace(SECRETISH, '[redacted]'), 'paypal', res.status, text(detail.issue) || text(e.name) || undefined);
|
|
246
|
+
}
|
|
247
|
+
return json;
|
|
248
|
+
}
|
|
249
|
+
const unitOf = (order) => list(order.purchase_units)[0] ?? {};
|
|
250
|
+
const captureOf = (order) => list(obj(unitOf(order).payments).captures)[0];
|
|
251
|
+
const toRecord = (order, id) => {
|
|
252
|
+
const unit = unitOf(order);
|
|
253
|
+
const capture = captureOf(order);
|
|
254
|
+
const money = obj(capture ? capture.amount : unit.amount);
|
|
255
|
+
const code = text(money.currency_code);
|
|
256
|
+
// Paid is read from the capture, never from the order: a COMPLETED order can hold a DECLINED capture.
|
|
257
|
+
const status = capture ? captureStatus(capture.status) : orderStatus(order.status);
|
|
258
|
+
const feeMoney = capture ? obj(obj(capture.seller_receivable_breakdown).paypal_fee) : {};
|
|
259
|
+
const fee = fromPayPalValue(feeMoney.value, text(feeMoney.currency_code));
|
|
260
|
+
const paidAt = capture && status === 'paid' ? new Date(text(capture.create_time)) : undefined;
|
|
261
|
+
return {
|
|
262
|
+
id: text(order.id) || id,
|
|
263
|
+
reference: text(unit.custom_id),
|
|
264
|
+
status,
|
|
265
|
+
amount: fromPayPalValue(money.value, code),
|
|
266
|
+
currency: code,
|
|
267
|
+
...(paidAt && !Number.isNaN(paidAt.getTime()) ? { paidAt } : {}),
|
|
268
|
+
fee: Number.isNaN(fee) ? null : fee,
|
|
269
|
+
raw: order,
|
|
270
|
+
};
|
|
271
|
+
};
|
|
272
|
+
const checkId = (id) => {
|
|
273
|
+
if (typeof id !== 'string' || !ID.test(id))
|
|
274
|
+
throw fail('invalid order id', 'INVALID_ID');
|
|
275
|
+
};
|
|
276
|
+
const checkRequestId = (key) => {
|
|
277
|
+
if (key !== undefined && (typeof key !== 'string' || !key || key.length > REQUEST_ID_MAX)) {
|
|
278
|
+
throw fail(`idempotencyKey must be 1 to ${REQUEST_ID_MAX} characters`, 'INVALID_KEY');
|
|
279
|
+
}
|
|
280
|
+
};
|
|
281
|
+
async function getPayment(id) {
|
|
282
|
+
checkId(id);
|
|
283
|
+
return toRecord(await request('GET', `/v2/checkout/orders/${id}`), id);
|
|
284
|
+
}
|
|
285
|
+
/** Postback check (see the header comment). Resolves null for anything not authentic; rejects when PayPal fails. */
|
|
286
|
+
async function verify(cb) {
|
|
287
|
+
let rawBody = '';
|
|
288
|
+
let event = {};
|
|
289
|
+
const fields = {};
|
|
290
|
+
try {
|
|
291
|
+
const headers = (cb?.headers ?? {});
|
|
292
|
+
for (const [field, name] of Object.entries(HEADERS))
|
|
293
|
+
fields[field] = headerOf(headers, name);
|
|
294
|
+
rawBody = typeof cb?.rawBody === 'string' ? cb.rawBody : '';
|
|
295
|
+
if (!rawBody || Object.values(fields).some((v) => !v))
|
|
296
|
+
return null;
|
|
297
|
+
// Parsing proves the raw text is exactly one JSON object, so splicing it below cannot add other fields.
|
|
298
|
+
const parsed = JSON.parse(rawBody);
|
|
299
|
+
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed))
|
|
300
|
+
return null;
|
|
301
|
+
event = parsed;
|
|
302
|
+
}
|
|
303
|
+
catch {
|
|
304
|
+
return null;
|
|
305
|
+
}
|
|
306
|
+
const head = JSON.stringify({ ...fields, webhook_id: config.webhookId });
|
|
307
|
+
const answer = await request('POST', '/v1/notifications/verify-webhook-signature', `${head.slice(0, -1)},"webhook_event":${rawBody}}`);
|
|
308
|
+
if (answer.verification_status !== 'SUCCESS')
|
|
309
|
+
return null;
|
|
310
|
+
// Authentic. Only checkout-order and capture events name an order; anything else is not a payment callback.
|
|
311
|
+
const type = text(event.event_type);
|
|
312
|
+
const resource = obj(event.resource);
|
|
313
|
+
let orderId = '';
|
|
314
|
+
let captureId = '';
|
|
315
|
+
let reference = '';
|
|
316
|
+
let claimedStatus = 'unknown';
|
|
317
|
+
if (type.startsWith('CHECKOUT.ORDER.')) {
|
|
318
|
+
const record = toRecord(resource, text(resource.id));
|
|
319
|
+
orderId = record.id;
|
|
320
|
+
captureId = text(captureOf(resource)?.id);
|
|
321
|
+
reference = record.reference;
|
|
322
|
+
claimedStatus = record.status;
|
|
323
|
+
}
|
|
324
|
+
else if (type.startsWith('PAYMENT.CAPTURE.')) {
|
|
325
|
+
orderId = text(obj(obj(resource.supplementary_data).related_ids).order_id);
|
|
326
|
+
captureId = text(resource.id);
|
|
327
|
+
reference = text(resource.custom_id);
|
|
328
|
+
claimedStatus = captureStatus(resource.status);
|
|
329
|
+
if (type === 'PAYMENT.CAPTURE.DENIED')
|
|
330
|
+
claimedStatus = 'failed';
|
|
331
|
+
}
|
|
332
|
+
if (!ID.test(orderId))
|
|
333
|
+
return null;
|
|
334
|
+
return {
|
|
335
|
+
id: orderId,
|
|
336
|
+
reference,
|
|
337
|
+
claimedStatus,
|
|
338
|
+
raw: {
|
|
339
|
+
event_id: text(event.id),
|
|
340
|
+
event_type: type,
|
|
341
|
+
order_id: orderId,
|
|
342
|
+
capture_id: captureId,
|
|
343
|
+
reference,
|
|
344
|
+
status: text(resource.status),
|
|
345
|
+
},
|
|
346
|
+
};
|
|
347
|
+
}
|
|
348
|
+
return {
|
|
349
|
+
info: gatewayInfo,
|
|
350
|
+
async createPayment(input) {
|
|
351
|
+
const code = input.currency ?? currency;
|
|
352
|
+
if (!CURRENCIES.includes(code))
|
|
353
|
+
throw fail(`PayPal does not take ${code}`, 'CURRENCY');
|
|
354
|
+
if (!Number.isSafeInteger(input.amount) || input.amount < gatewayInfo.minimumAmount) {
|
|
355
|
+
throw fail(`amount must be a whole number of ${code} minor units, at least 1`, 'INVALID_AMOUNT');
|
|
356
|
+
}
|
|
357
|
+
if (!input.reference || input.reference.length > REFERENCE_MAX) {
|
|
358
|
+
throw fail(`reference must be 1 to ${REFERENCE_MAX} characters`, 'INVALID_REFERENCE');
|
|
359
|
+
}
|
|
360
|
+
checkRequestId(input.idempotencyKey);
|
|
361
|
+
const description = input.description?.trim().slice(0, 127);
|
|
362
|
+
const order = await request('POST', '/v2/checkout/orders', {
|
|
363
|
+
intent: 'CAPTURE',
|
|
364
|
+
purchase_units: [
|
|
365
|
+
{
|
|
366
|
+
custom_id: input.reference,
|
|
367
|
+
amount: { currency_code: code, value: toPayPalValue(input.amount, code) },
|
|
368
|
+
...(description ? { description } : {}),
|
|
369
|
+
},
|
|
370
|
+
],
|
|
371
|
+
payment_source: {
|
|
372
|
+
paypal: {
|
|
373
|
+
experience_context: {
|
|
374
|
+
return_url: input.returnUrl,
|
|
375
|
+
cancel_url: input.returnUrl,
|
|
376
|
+
user_action: 'PAY_NOW',
|
|
377
|
+
shipping_preference: config.shippingPreference ?? 'NO_SHIPPING',
|
|
378
|
+
...(config.brandName ? { brand_name: config.brandName.slice(0, 127) } : {}),
|
|
379
|
+
},
|
|
380
|
+
},
|
|
381
|
+
},
|
|
382
|
+
}, input.idempotencyKey);
|
|
383
|
+
const id = text(order.id);
|
|
384
|
+
const link = list(order.links).find((l) => l.rel === 'payer-action' || l.rel === 'approve');
|
|
385
|
+
const url = text(link?.href);
|
|
386
|
+
if (!ID.test(id) || !/^https?:\/\//.test(url)) {
|
|
387
|
+
throw new GatewayError('order without id or approval link', 'paypal', 201);
|
|
388
|
+
}
|
|
389
|
+
return { id, url, raw: order };
|
|
390
|
+
},
|
|
391
|
+
getPayment,
|
|
392
|
+
async capturePayment(orderId, opts = {}) {
|
|
393
|
+
checkId(orderId);
|
|
394
|
+
checkRequestId(opts.idempotencyKey);
|
|
395
|
+
const order = await request('POST', `/v2/checkout/orders/${orderId}/capture`, {}, opts.idempotencyKey ?? `capture-${orderId}`);
|
|
396
|
+
return toRecord(order, orderId);
|
|
397
|
+
},
|
|
398
|
+
async refundPayment(input) {
|
|
399
|
+
checkRequestId(input.idempotencyKey);
|
|
400
|
+
const record = await getPayment(input.paymentId);
|
|
401
|
+
const capture = captureOf(obj(record.raw));
|
|
402
|
+
if (record.status !== 'paid' || !capture)
|
|
403
|
+
throw fail(`payment is ${record.status}, not paid`, 'NOT_PAID');
|
|
404
|
+
const captureId = text(capture.id);
|
|
405
|
+
if (!ID.test(captureId))
|
|
406
|
+
throw fail('the order has no capture id', 'NOT_PAID');
|
|
407
|
+
const refunded = list(obj(unitOf(obj(record.raw)).payments).refunds)
|
|
408
|
+
.filter((r) => r.status !== 'FAILED' && r.status !== 'CANCELLED')
|
|
409
|
+
.reduce((sum, r) => sum + fromPayPalValue(obj(r.amount).value, record.currency), 0);
|
|
410
|
+
const left = capture.status === 'REFUNDED' ? 0 : record.amount - (Number.isNaN(refunded) ? 0 : refunded);
|
|
411
|
+
checkRefundAmount('paypal', input, left);
|
|
412
|
+
const reason = input.reason?.trim().slice(0, 255);
|
|
413
|
+
const r = await request('POST', `/v2/payments/captures/${captureId}/refund`, {
|
|
414
|
+
amount: { currency_code: record.currency, value: toPayPalValue(input.amount, record.currency) },
|
|
415
|
+
...(reason ? { note_to_payer: reason } : {}),
|
|
416
|
+
}, input.idempotencyKey);
|
|
417
|
+
const money = obj(r.amount);
|
|
418
|
+
const code = text(money.currency_code) || record.currency;
|
|
419
|
+
return {
|
|
420
|
+
id: text(r.id),
|
|
421
|
+
paymentId: input.paymentId,
|
|
422
|
+
status: refundStatus(r.status),
|
|
423
|
+
amount: fromPayPalValue(money.value, code),
|
|
424
|
+
currency: code,
|
|
425
|
+
raw: r,
|
|
426
|
+
};
|
|
427
|
+
},
|
|
428
|
+
verifyCallback(cb) {
|
|
429
|
+
return verify(cb);
|
|
430
|
+
},
|
|
431
|
+
};
|
|
432
|
+
}
|
|
433
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,2 +1,53 @@
|
|
|
1
|
-
{
|
|
2
|
-
|
|
1
|
+
{
|
|
2
|
+
"name": "@bayarkit/paypal",
|
|
3
|
+
"version": "0.3.0",
|
|
4
|
+
"description": "PayPal Orders v2 for Node.js and TypeScript: create an order, capture it after the buyer approves, verify webhooks through PayPal's own check, confirm each payment with a fresh order query, and refund. Zero dependencies, part of bayarkit.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"payment",
|
|
7
|
+
"payment-gateway",
|
|
8
|
+
"malaysia",
|
|
9
|
+
"fpx",
|
|
10
|
+
"duitnow",
|
|
11
|
+
"webhook",
|
|
12
|
+
"checkout",
|
|
13
|
+
"typescript",
|
|
14
|
+
"nodejs",
|
|
15
|
+
"paypal"
|
|
16
|
+
],
|
|
17
|
+
"homepage": "https://github.com/sasateguh/bayarkit/tree/main/packages/paypal#readme",
|
|
18
|
+
"bugs": {
|
|
19
|
+
"url": "https://github.com/sasateguh/bayarkit/issues"
|
|
20
|
+
},
|
|
21
|
+
"repository": {
|
|
22
|
+
"type": "git",
|
|
23
|
+
"url": "git+https://github.com/sasateguh/bayarkit.git",
|
|
24
|
+
"directory": "packages/paypal"
|
|
25
|
+
},
|
|
26
|
+
"license": "MIT",
|
|
27
|
+
"author": "Muhammad Aiman (https://github.com/aimanmalib)",
|
|
28
|
+
"type": "module",
|
|
29
|
+
"sideEffects": false,
|
|
30
|
+
"exports": {
|
|
31
|
+
".": {
|
|
32
|
+
"types": "./dist/index.d.ts",
|
|
33
|
+
"import": "./dist/index.js"
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
"files": [
|
|
37
|
+
"dist",
|
|
38
|
+
"README.md"
|
|
39
|
+
],
|
|
40
|
+
"engines": {
|
|
41
|
+
"node": ">=22.12.0"
|
|
42
|
+
},
|
|
43
|
+
"publishConfig": {
|
|
44
|
+
"access": "public",
|
|
45
|
+
"provenance": true
|
|
46
|
+
},
|
|
47
|
+
"scripts": {
|
|
48
|
+
"build": "tsc -p tsconfig.build.json"
|
|
49
|
+
},
|
|
50
|
+
"dependencies": {
|
|
51
|
+
"@bayarkit/core": "0.3.0"
|
|
52
|
+
}
|
|
53
|
+
}
|