@rovela-ai/sdk 0.24.1 → 0.25.1
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/dist/admin/api/index.d.ts +1 -0
- package/dist/admin/api/index.d.ts.map +1 -1
- package/dist/admin/api/index.js +2 -0
- package/dist/admin/api/index.js.map +1 -1
- package/dist/admin/api/payment-providers.d.ts +69 -0
- package/dist/admin/api/payment-providers.d.ts.map +1 -0
- package/dist/admin/api/payment-providers.js +206 -0
- package/dist/admin/api/payment-providers.js.map +1 -0
- package/dist/admin/api/settings.d.ts.map +1 -1
- package/dist/admin/api/settings.js +4 -1
- package/dist/admin/api/settings.js.map +1 -1
- package/dist/admin/components/OrderDetails.d.ts.map +1 -1
- package/dist/admin/components/OrderDetails.js +11 -2
- package/dist/admin/components/OrderDetails.js.map +1 -1
- package/dist/admin/components/PaymentProviderPanels.d.ts +2 -0
- package/dist/admin/components/PaymentProviderPanels.d.ts.map +1 -0
- package/dist/admin/components/PaymentProviderPanels.js +265 -0
- package/dist/admin/components/PaymentProviderPanels.js.map +1 -0
- package/dist/admin/components/PaymentSettings.d.ts.map +1 -1
- package/dist/admin/components/PaymentSettings.js +7 -20
- package/dist/admin/components/PaymentSettings.js.map +1 -1
- package/dist/admin/components/emails/EmailSendingPanel.d.ts.map +1 -1
- package/dist/admin/components/emails/EmailSendingPanel.js +2 -2
- package/dist/admin/components/emails/EmailSendingPanel.js.map +1 -1
- package/dist/admin/components/index.d.ts +1 -0
- package/dist/admin/components/index.d.ts.map +1 -1
- package/dist/admin/components/index.js +1 -0
- package/dist/admin/components/index.js.map +1 -1
- package/dist/admin/integrations/registry.d.ts.map +1 -1
- package/dist/admin/integrations/registry.js +5 -73
- package/dist/admin/integrations/registry.js.map +1 -1
- package/dist/cart/store.d.ts +4 -1
- package/dist/cart/store.d.ts.map +1 -1
- package/dist/cart/store.js +189 -161
- package/dist/cart/store.js.map +1 -1
- package/dist/checkout/api/index.d.ts +6 -0
- package/dist/checkout/api/index.d.ts.map +1 -1
- package/dist/checkout/api/index.js +6 -0
- package/dist/checkout/api/index.js.map +1 -1
- package/dist/checkout/api/manual.d.ts.map +1 -1
- package/dist/checkout/api/manual.js +5 -1
- package/dist/checkout/api/manual.js.map +1 -1
- package/dist/checkout/api/payment-methods.d.ts +23 -6
- package/dist/checkout/api/payment-methods.d.ts.map +1 -1
- package/dist/checkout/api/payment-methods.js +67 -8
- package/dist/checkout/api/payment-methods.js.map +1 -1
- package/dist/checkout/api/paypal-capture.d.ts +38 -0
- package/dist/checkout/api/paypal-capture.d.ts.map +1 -0
- package/dist/checkout/api/paypal-capture.js +126 -0
- package/dist/checkout/api/paypal-capture.js.map +1 -0
- package/dist/checkout/api/paypal-webhook.d.ts +24 -0
- package/dist/checkout/api/paypal-webhook.d.ts.map +1 -0
- package/dist/checkout/api/paypal-webhook.js +122 -0
- package/dist/checkout/api/paypal-webhook.js.map +1 -0
- package/dist/checkout/api/paypal.d.ts +30 -0
- package/dist/checkout/api/paypal.d.ts.map +1 -0
- package/dist/checkout/api/paypal.js +70 -0
- package/dist/checkout/api/paypal.js.map +1 -0
- package/dist/checkout/api/up2pay-ipn.d.ts +28 -0
- package/dist/checkout/api/up2pay-ipn.d.ts.map +1 -0
- package/dist/checkout/api/up2pay-ipn.js +88 -0
- package/dist/checkout/api/up2pay-ipn.js.map +1 -0
- package/dist/checkout/api/up2pay.d.ts +34 -0
- package/dist/checkout/api/up2pay.d.ts.map +1 -0
- package/dist/checkout/api/up2pay.js +82 -0
- package/dist/checkout/api/up2pay.js.map +1 -0
- package/dist/checkout/components/CheckoutFlow.d.ts.map +1 -1
- package/dist/checkout/components/CheckoutFlow.js +64 -9
- package/dist/checkout/components/CheckoutFlow.js.map +1 -1
- package/dist/checkout/components/PayPalCheckoutButtons.d.ts +32 -0
- package/dist/checkout/components/PayPalCheckoutButtons.d.ts.map +1 -0
- package/dist/checkout/components/PayPalCheckoutButtons.js +116 -0
- package/dist/checkout/components/PayPalCheckoutButtons.js.map +1 -0
- package/dist/checkout/components/index.d.ts +2 -0
- package/dist/checkout/components/index.d.ts.map +1 -1
- package/dist/checkout/components/index.js +1 -0
- package/dist/checkout/components/index.js.map +1 -1
- package/dist/checkout/components/payment-logos.d.ts +21 -0
- package/dist/checkout/components/payment-logos.d.ts.map +1 -0
- package/dist/checkout/components/payment-logos.js +21 -0
- package/dist/checkout/components/payment-logos.js.map +1 -0
- package/dist/checkout/hooks/useCheckout.d.ts.map +1 -1
- package/dist/checkout/hooks/useCheckout.js +160 -0
- package/dist/checkout/hooks/useCheckout.js.map +1 -1
- package/dist/checkout/server/confirm-order.d.ts +54 -0
- package/dist/checkout/server/confirm-order.d.ts.map +1 -0
- package/dist/checkout/server/confirm-order.js +147 -0
- package/dist/checkout/server/confirm-order.js.map +1 -0
- package/dist/checkout/server/external-checkout.d.ts +68 -0
- package/dist/checkout/server/external-checkout.d.ts.map +1 -0
- package/dist/checkout/server/external-checkout.js +187 -0
- package/dist/checkout/server/external-checkout.js.map +1 -0
- package/dist/checkout/server/paypal.d.ts +127 -0
- package/dist/checkout/server/paypal.d.ts.map +1 -0
- package/dist/checkout/server/paypal.js +274 -0
- package/dist/checkout/server/paypal.js.map +1 -0
- package/dist/checkout/server/up2pay.d.ts +152 -0
- package/dist/checkout/server/up2pay.d.ts.map +1 -0
- package/dist/checkout/server/up2pay.js +287 -0
- package/dist/checkout/server/up2pay.js.map +1 -0
- package/dist/checkout/types.d.ts +26 -1
- package/dist/checkout/types.d.ts.map +1 -1
- package/dist/core/db/queries.d.ts +68 -39
- package/dist/core/db/queries.d.ts.map +1 -1
- package/dist/core/db/queries.js +96 -0
- package/dist/core/db/queries.js.map +1 -1
- package/dist/core/db/schema.d.ts +210 -5
- package/dist/core/db/schema.d.ts.map +1 -1
- package/dist/core/db/schema.js +33 -0
- package/dist/core/db/schema.js.map +1 -1
- package/dist/core/i18n/messages/en.d.ts.map +1 -1
- package/dist/core/i18n/messages/en.js +6 -0
- package/dist/core/i18n/messages/en.js.map +1 -1
- package/dist/core/i18n/messages/fr.d.ts.map +1 -1
- package/dist/core/i18n/messages/fr.js +6 -0
- package/dist/core/i18n/messages/fr.js.map +1 -1
- package/docs/changelog/epoch-12-payment-providers.md +152 -0
- package/package.json +26 -2
- package/templates/store-template/.claude/skills/build-feature/SKILL.md +2 -1
- package/templates/store-template/app/api/admin/payments/paypal/route.ts +10 -0
- package/templates/store-template/app/api/admin/payments/up2pay/route.ts +10 -0
- package/templates/store-template/app/api/checkout/paypal/capture/route.ts +8 -0
- package/templates/store-template/app/api/checkout/paypal/return/route.ts +9 -0
- package/templates/store-template/app/api/checkout/paypal/route.ts +9 -0
- package/templates/store-template/app/api/checkout/paypal/webhook/route.ts +9 -0
- package/templates/store-template/app/api/checkout/up2pay/ipn/route.ts +8 -0
- package/templates/store-template/app/api/checkout/up2pay/route.ts +9 -0
- package/templates/store-template/package.json +2 -2
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @rovela/sdk/checkout/server/external-checkout
|
|
3
|
+
*
|
|
4
|
+
* Shared "start an external-provider payment" leg (PayPal, Up2pay): the
|
|
5
|
+
* same gates → buildQuote → pending-order sequence as the manual path
|
|
6
|
+
* (checkout/api/manual.ts), minus the confirmation email — external orders
|
|
7
|
+
* email on CONFIRMED payment (confirm-order.ts), not at creation.
|
|
8
|
+
*
|
|
9
|
+
* §55 invariants preserved verbatim: session-resolved identity, the ONE
|
|
10
|
+
* pricing engine in the ONE mode, PricingError → 4xx, idempotent reference
|
|
11
|
+
* through the shared payment-intent column. Discount redemptions + purchase
|
|
12
|
+
* analytics record at CREATE time — deliberate parity with the manual path
|
|
13
|
+
* (a pending order that is never paid keeps its redemption, exactly like an
|
|
14
|
+
* unpaid COD order; consistency beats a second bookkeeping model).
|
|
15
|
+
*/
|
|
16
|
+
import { randomUUID } from 'crypto';
|
|
17
|
+
import { NextResponse } from 'next/server';
|
|
18
|
+
import { findSettings, recordDiscountRedemption } from '../../core/db/queries';
|
|
19
|
+
import { buildQuote, PricingError, CHECKOUT_PRICING_MODE } from './pricing';
|
|
20
|
+
import { resolveCheckoutCustomer } from './checkout-customer';
|
|
21
|
+
import { createOrderFromCheckout } from './order-service';
|
|
22
|
+
import { recordEvent } from '../../analytics/server';
|
|
23
|
+
import { isSupportedLocale } from '../../core/i18n/types';
|
|
24
|
+
const bad = (error, status = 400) => ({
|
|
25
|
+
ok: false,
|
|
26
|
+
response: NextResponse.json({ error, code: 'INVALID_REQUEST' }, { status }),
|
|
27
|
+
});
|
|
28
|
+
export async function preparePendingExternalOrder(input) {
|
|
29
|
+
const { body, provider } = input;
|
|
30
|
+
const settings = await findSettings();
|
|
31
|
+
// Trusted identity: session cookie, never body (§55).
|
|
32
|
+
const trustedCustomer = await resolveCheckoutCustomer();
|
|
33
|
+
const allowGuestCheckout = settings?.guestCheckout ?? true;
|
|
34
|
+
if (!allowGuestCheckout && !trustedCustomer) {
|
|
35
|
+
return {
|
|
36
|
+
ok: false,
|
|
37
|
+
response: NextResponse.json({
|
|
38
|
+
error: 'Guest checkout is disabled. Please sign in to continue.',
|
|
39
|
+
code: 'GUEST_CHECKOUT_DISABLED',
|
|
40
|
+
}, { status: 403 }),
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
// Inline-signed-in customers never type an email (the guest contact field
|
|
44
|
+
// is skipped), so the body may not carry one — the SESSION-resolved email
|
|
45
|
+
// is the fallback, and the better truth when present (0.25.1).
|
|
46
|
+
const email = body.customerEmail?.trim().toLowerCase() || trustedCustomer?.email?.trim().toLowerCase();
|
|
47
|
+
if (!email || !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
|
|
48
|
+
return bad('A valid email is required');
|
|
49
|
+
}
|
|
50
|
+
// ── The Quote: the SAME engine in the SAME mode as every money path ──────
|
|
51
|
+
let quote;
|
|
52
|
+
try {
|
|
53
|
+
quote = await buildQuote({
|
|
54
|
+
items: body.items,
|
|
55
|
+
shippingAddress: body.shippingAddress,
|
|
56
|
+
selectedShipping: body.selectedShipping,
|
|
57
|
+
pricingMode: CHECKOUT_PRICING_MODE,
|
|
58
|
+
discountCode: body.discountCode ?? null,
|
|
59
|
+
customerId: trustedCustomer?.id ?? null,
|
|
60
|
+
customerGroupId: trustedCustomer?.groupId ?? null,
|
|
61
|
+
customerEmail: email,
|
|
62
|
+
}, { settings });
|
|
63
|
+
}
|
|
64
|
+
catch (err) {
|
|
65
|
+
if (err instanceof PricingError) {
|
|
66
|
+
return {
|
|
67
|
+
ok: false,
|
|
68
|
+
response: NextResponse.json({ error: err.message, code: err.code }, { status: err.status }),
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
throw err;
|
|
72
|
+
}
|
|
73
|
+
// ── Idempotency reference (shared payment-intent column) ─────────────────
|
|
74
|
+
const rawKey = typeof body.idempotencyKey === 'string' ? body.idempotencyKey : '';
|
|
75
|
+
const safeKey = rawKey.replace(/[^a-zA-Z0-9_-]/g, '').slice(0, 48);
|
|
76
|
+
const reference = `${provider}_${safeKey || randomUUID()}`;
|
|
77
|
+
// ── Address snapshots (identical rules to the manual path) ───────────────
|
|
78
|
+
const orderShippingAddress = quote.pickupAddress
|
|
79
|
+
? {
|
|
80
|
+
firstName: body.shippingAddress?.firstName || trustedCustomer?.name?.split(' ')[0] || '',
|
|
81
|
+
lastName: body.shippingAddress?.lastName ||
|
|
82
|
+
trustedCustomer?.name?.split(' ').slice(1).join(' ') ||
|
|
83
|
+
'',
|
|
84
|
+
...quote.pickupAddress,
|
|
85
|
+
}
|
|
86
|
+
: quote.relayAddress
|
|
87
|
+
? {
|
|
88
|
+
firstName: body.shippingAddress?.firstName || '',
|
|
89
|
+
lastName: body.shippingAddress?.lastName || '',
|
|
90
|
+
...quote.relayAddress,
|
|
91
|
+
phone: body.shippingAddress?.phone,
|
|
92
|
+
}
|
|
93
|
+
: body.shippingAddress;
|
|
94
|
+
const orderBillingAddress = quote.relay && !body.billingAddress ? body.shippingAddress : body.billingAddress;
|
|
95
|
+
const orderLocale = isSupportedLocale(body.locale) ? body.locale : null;
|
|
96
|
+
const { orderId, isNew } = await createOrderFromCheckout({
|
|
97
|
+
locale: orderLocale,
|
|
98
|
+
sessionId: reference,
|
|
99
|
+
email,
|
|
100
|
+
items: quote.lines,
|
|
101
|
+
subtotal: quote.subtotalCents,
|
|
102
|
+
tax: quote.taxCents,
|
|
103
|
+
taxBreakdown: quote.taxBreakdown,
|
|
104
|
+
shipping: quote.shippingCents,
|
|
105
|
+
total: quote.totalCents,
|
|
106
|
+
currency: quote.currency,
|
|
107
|
+
discountCode: quote.discountCode ?? undefined,
|
|
108
|
+
discountAmount: quote.discountCents > 0 ? quote.discountCents : undefined,
|
|
109
|
+
shippingAddress: orderShippingAddress,
|
|
110
|
+
billingAddress: orderBillingAddress,
|
|
111
|
+
stripePaymentIntentId: reference,
|
|
112
|
+
customerId: trustedCustomer?.id,
|
|
113
|
+
shippingMethod: quote.shippingMethodName,
|
|
114
|
+
carrier: quote.carrier,
|
|
115
|
+
shippoRateId: quote.shippoRateId,
|
|
116
|
+
pickupLocationId: quote.pickupLocationId ?? undefined,
|
|
117
|
+
relay: quote.relay ?? undefined,
|
|
118
|
+
status: 'pending',
|
|
119
|
+
paymentProvider: provider,
|
|
120
|
+
paymentMethodLabel: input.providerLabel,
|
|
121
|
+
});
|
|
122
|
+
if (isNew && quote.appliedDiscounts.length > 0) {
|
|
123
|
+
for (const applied of quote.appliedDiscounts) {
|
|
124
|
+
try {
|
|
125
|
+
await recordDiscountRedemption({
|
|
126
|
+
discountId: applied.id,
|
|
127
|
+
orderId,
|
|
128
|
+
customerId: trustedCustomer?.id ?? null,
|
|
129
|
+
email,
|
|
130
|
+
amountCents: applied.amountCents,
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
catch (err) {
|
|
134
|
+
console.error(`[${provider} Checkout] redemption record failed:`, err);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
// Server-side purchase analytics — the (order_id) partial unique index
|
|
139
|
+
// dedupes; recorded at create (manual-path parity, see file header).
|
|
140
|
+
if (isNew) {
|
|
141
|
+
const visitorId = body.metadata?.rovela_visitor_id;
|
|
142
|
+
const sessionId = body.metadata?.rovela_session_id;
|
|
143
|
+
if (visitorId && sessionId) {
|
|
144
|
+
try {
|
|
145
|
+
await recordEvent({
|
|
146
|
+
event: {
|
|
147
|
+
event: 'purchase',
|
|
148
|
+
visitor_id: visitorId,
|
|
149
|
+
session_id: sessionId,
|
|
150
|
+
order_id: orderId,
|
|
151
|
+
value_cents: quote.totalCents,
|
|
152
|
+
currency: quote.currency,
|
|
153
|
+
path: '/checkout/success',
|
|
154
|
+
},
|
|
155
|
+
device: null,
|
|
156
|
+
country: null,
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
catch (err) {
|
|
160
|
+
console.error(`[${provider} Checkout] purchase analytics failed:`, err);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
return {
|
|
165
|
+
ok: true,
|
|
166
|
+
orderId,
|
|
167
|
+
isNew,
|
|
168
|
+
reference,
|
|
169
|
+
quote,
|
|
170
|
+
email,
|
|
171
|
+
orderLocale,
|
|
172
|
+
orderShippingAddress,
|
|
173
|
+
orderBillingAddress,
|
|
174
|
+
settings,
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* The absolute base URL for return/IPN links handed to a PSP. Prefers the
|
|
179
|
+
* deployed store's canonical env; falls back to the request origin (sandbox
|
|
180
|
+
* previews).
|
|
181
|
+
*/
|
|
182
|
+
export function resolveStoreBaseUrl(requestOrigin) {
|
|
183
|
+
const envUrl = process.env.NEXT_PUBLIC_APP_URL?.trim();
|
|
184
|
+
const base = envUrl || requestOrigin;
|
|
185
|
+
return base.replace(/\/$/, '');
|
|
186
|
+
}
|
|
187
|
+
//# sourceMappingURL=external-checkout.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"external-checkout.js","sourceRoot":"","sources":["../../../src/checkout/server/external-checkout.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @rovela/sdk/checkout/server/paypal
|
|
3
|
+
*
|
|
4
|
+
* PayPal (Orders v2) — hand-rolled fetch client, no PayPal npm SDK (the
|
|
5
|
+
* Colissimo/GSC house pattern: 4 REST endpoints don't justify a
|
|
6
|
+
* dependency). Server-side ONLY — PayPal's own guidance: never call the
|
|
7
|
+
* Orders API from the browser.
|
|
8
|
+
*
|
|
9
|
+
* Flow: create order server-priced (invoice_id = our up-front reference,
|
|
10
|
+
* PayPal-Request-Id idempotency) → buyer approves (Smart Buttons popup, or
|
|
11
|
+
* the approve-link redirect fallback) → server capture → the shared
|
|
12
|
+
* confirm-order helper flips the pending order to paid. The webhook is the
|
|
13
|
+
* async BACKSTOP (buyer closed the tab between approval and capture),
|
|
14
|
+
* never the primary path.
|
|
15
|
+
*
|
|
16
|
+
* SECURITY INVARIANTS (do not weaken):
|
|
17
|
+
* - client_secret never crosses a client boundary (server-only
|
|
18
|
+
* store_settings column, stripped from the admin settings GET; the
|
|
19
|
+
* public clientId is the only client-visible credential).
|
|
20
|
+
* - Amounts always come from buildQuote via the DB order — the client
|
|
21
|
+
* only ever passes the PayPal order id.
|
|
22
|
+
* - Webhook events are only trusted after verify-webhook-signature.
|
|
23
|
+
*/
|
|
24
|
+
declare const PAYPAL_HOSTS: {
|
|
25
|
+
readonly production: "https://api-m.paypal.com";
|
|
26
|
+
readonly sandbox: "https://api-m.sandbox.paypal.com";
|
|
27
|
+
};
|
|
28
|
+
export type PaypalEnvironment = keyof typeof PAYPAL_HOSTS;
|
|
29
|
+
/**
|
|
30
|
+
* Normalize a stored environment value. Accepts BOTH historical vocabularies
|
|
31
|
+
* ('live' from the retired integrations-registry entry, 'production' from the
|
|
32
|
+
* retired feature card) — never migrated by sweep, healed at read time.
|
|
33
|
+
*/
|
|
34
|
+
export declare function normalizePaypalEnvironment(value: string | null | undefined): PaypalEnvironment;
|
|
35
|
+
export interface PaypalCredentials {
|
|
36
|
+
clientId: string;
|
|
37
|
+
clientSecret: string;
|
|
38
|
+
environment: PaypalEnvironment;
|
|
39
|
+
webhookId: string | null;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Resolve usable credentials from the store settings row, or null when the
|
|
43
|
+
* provider isn't fully configured + enabled. The ONE availability gate.
|
|
44
|
+
*/
|
|
45
|
+
export declare function resolvePaypalCredentials(settings: {
|
|
46
|
+
paypalClientId?: string | null;
|
|
47
|
+
paypalClientSecret?: string | null;
|
|
48
|
+
paypalEnvironment?: string | null;
|
|
49
|
+
paypalWebhookId?: string | null;
|
|
50
|
+
paypalEnabled?: boolean | null;
|
|
51
|
+
} | null): PaypalCredentials | null;
|
|
52
|
+
export declare class PaypalApiError extends Error {
|
|
53
|
+
status: number;
|
|
54
|
+
/** PayPal issue code when present (e.g. ORDER_ALREADY_CAPTURED). */
|
|
55
|
+
issue: string | null;
|
|
56
|
+
body: unknown;
|
|
57
|
+
constructor(message: string, status: number, body: unknown);
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Credential probe used by the admin Payments panel's validate-before-persist
|
|
61
|
+
* (an OAuth token grant is the cheapest authenticated call). Returns an error
|
|
62
|
+
* message or null when the credentials are accepted.
|
|
63
|
+
*/
|
|
64
|
+
export declare function validatePaypalCredentials(creds: {
|
|
65
|
+
clientId: string;
|
|
66
|
+
clientSecret: string;
|
|
67
|
+
environment: PaypalEnvironment;
|
|
68
|
+
}): Promise<string | null>;
|
|
69
|
+
export interface CreatePaypalOrderInput {
|
|
70
|
+
credentials: PaypalCredentials;
|
|
71
|
+
/** Our order reference (paypal_{uuid}) — round-trips via invoice_id. */
|
|
72
|
+
reference: string;
|
|
73
|
+
totalCents: number;
|
|
74
|
+
/** Uppercase ISO currency. */
|
|
75
|
+
currency: string;
|
|
76
|
+
storeName: string;
|
|
77
|
+
returnUrl: string;
|
|
78
|
+
cancelUrl: string;
|
|
79
|
+
/** Checkout-collected shipping address; null for pickup (NO_SHIPPING). */
|
|
80
|
+
shippingAddress?: {
|
|
81
|
+
firstName?: string;
|
|
82
|
+
lastName?: string;
|
|
83
|
+
line1?: string;
|
|
84
|
+
line2?: string | null;
|
|
85
|
+
city?: string;
|
|
86
|
+
state?: string | null;
|
|
87
|
+
postalCode?: string | null;
|
|
88
|
+
country?: string;
|
|
89
|
+
} | null;
|
|
90
|
+
locale?: string | null;
|
|
91
|
+
}
|
|
92
|
+
export interface CreatePaypalOrderResult {
|
|
93
|
+
paypalOrderId: string;
|
|
94
|
+
approveUrl: string | null;
|
|
95
|
+
}
|
|
96
|
+
export declare function createPaypalOrder(input: CreatePaypalOrderInput): Promise<CreatePaypalOrderResult>;
|
|
97
|
+
export interface PaypalCaptureResult {
|
|
98
|
+
/** 'COMPLETED' = money captured. Anything else = NOT paid (webhook may confirm later). */
|
|
99
|
+
status: string;
|
|
100
|
+
captureId: string | null;
|
|
101
|
+
/** Our order reference (invoice_id round trip). */
|
|
102
|
+
invoiceId: string | null;
|
|
103
|
+
amountCents: number | null;
|
|
104
|
+
currency: string | null;
|
|
105
|
+
payerEmail: string | null;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Capture an approved PayPal order. Idempotent: a repeat capture (retry, or
|
|
109
|
+
* the return-leg racing the buttons leg) resolves via ORDER_ALREADY_CAPTURED
|
|
110
|
+
* → the order details are fetched and returned as a success.
|
|
111
|
+
*/
|
|
112
|
+
export declare function capturePaypalOrder(credentials: PaypalCredentials, paypalOrderId: string): Promise<PaypalCaptureResult>;
|
|
113
|
+
export interface PaypalWebhookHeaders {
|
|
114
|
+
transmissionId: string | null;
|
|
115
|
+
transmissionTime: string | null;
|
|
116
|
+
certUrl: string | null;
|
|
117
|
+
authAlgo: string | null;
|
|
118
|
+
transmissionSig: string | null;
|
|
119
|
+
}
|
|
120
|
+
export declare function readPaypalWebhookHeaders(headers: Headers): PaypalWebhookHeaders;
|
|
121
|
+
/**
|
|
122
|
+
* Verify a webhook delivery via PayPal's verify-webhook-signature endpoint
|
|
123
|
+
* (requires the merchant's webhook_id — configured in the Payments panel).
|
|
124
|
+
*/
|
|
125
|
+
export declare function verifyPaypalWebhook(credentials: PaypalCredentials, webhookId: string, headers: PaypalWebhookHeaders, event: unknown): Promise<boolean>;
|
|
126
|
+
export {};
|
|
127
|
+
//# sourceMappingURL=paypal.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"paypal.d.ts","sourceRoot":"","sources":["../../../src/checkout/server/paypal.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @rovela/sdk/checkout/server/paypal
|
|
3
|
+
*
|
|
4
|
+
* PayPal (Orders v2) — hand-rolled fetch client, no PayPal npm SDK (the
|
|
5
|
+
* Colissimo/GSC house pattern: 4 REST endpoints don't justify a
|
|
6
|
+
* dependency). Server-side ONLY — PayPal's own guidance: never call the
|
|
7
|
+
* Orders API from the browser.
|
|
8
|
+
*
|
|
9
|
+
* Flow: create order server-priced (invoice_id = our up-front reference,
|
|
10
|
+
* PayPal-Request-Id idempotency) → buyer approves (Smart Buttons popup, or
|
|
11
|
+
* the approve-link redirect fallback) → server capture → the shared
|
|
12
|
+
* confirm-order helper flips the pending order to paid. The webhook is the
|
|
13
|
+
* async BACKSTOP (buyer closed the tab between approval and capture),
|
|
14
|
+
* never the primary path.
|
|
15
|
+
*
|
|
16
|
+
* SECURITY INVARIANTS (do not weaken):
|
|
17
|
+
* - client_secret never crosses a client boundary (server-only
|
|
18
|
+
* store_settings column, stripped from the admin settings GET; the
|
|
19
|
+
* public clientId is the only client-visible credential).
|
|
20
|
+
* - Amounts always come from buildQuote via the DB order — the client
|
|
21
|
+
* only ever passes the PayPal order id.
|
|
22
|
+
* - Webhook events are only trusted after verify-webhook-signature.
|
|
23
|
+
*/
|
|
24
|
+
// =============================================================================
|
|
25
|
+
// Environments + credentials
|
|
26
|
+
// =============================================================================
|
|
27
|
+
const PAYPAL_HOSTS = {
|
|
28
|
+
production: 'https://api-m.paypal.com',
|
|
29
|
+
sandbox: 'https://api-m.sandbox.paypal.com',
|
|
30
|
+
};
|
|
31
|
+
/**
|
|
32
|
+
* Normalize a stored environment value. Accepts BOTH historical vocabularies
|
|
33
|
+
* ('live' from the retired integrations-registry entry, 'production' from the
|
|
34
|
+
* retired feature card) — never migrated by sweep, healed at read time.
|
|
35
|
+
*/
|
|
36
|
+
export function normalizePaypalEnvironment(value) {
|
|
37
|
+
return value === 'live' || value === 'production' ? 'production' : 'sandbox';
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Resolve usable credentials from the store settings row, or null when the
|
|
41
|
+
* provider isn't fully configured + enabled. The ONE availability gate.
|
|
42
|
+
*/
|
|
43
|
+
export function resolvePaypalCredentials(settings) {
|
|
44
|
+
if (!settings?.paypalEnabled)
|
|
45
|
+
return null;
|
|
46
|
+
const clientId = settings.paypalClientId?.trim();
|
|
47
|
+
const clientSecret = settings.paypalClientSecret?.trim();
|
|
48
|
+
if (!clientId || !clientSecret)
|
|
49
|
+
return null;
|
|
50
|
+
return {
|
|
51
|
+
clientId,
|
|
52
|
+
clientSecret,
|
|
53
|
+
environment: normalizePaypalEnvironment(settings.paypalEnvironment),
|
|
54
|
+
webhookId: settings.paypalWebhookId?.trim() || null,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
// =============================================================================
|
|
58
|
+
// API plumbing
|
|
59
|
+
// =============================================================================
|
|
60
|
+
export class PaypalApiError extends Error {
|
|
61
|
+
status;
|
|
62
|
+
/** PayPal issue code when present (e.g. ORDER_ALREADY_CAPTURED). */
|
|
63
|
+
issue;
|
|
64
|
+
body;
|
|
65
|
+
constructor(message, status, body) {
|
|
66
|
+
super(message);
|
|
67
|
+
this.name = 'PaypalApiError';
|
|
68
|
+
this.status = status;
|
|
69
|
+
this.body = body;
|
|
70
|
+
this.issue = extractIssue(body);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
function extractIssue(body) {
|
|
74
|
+
if (!body || typeof body !== 'object')
|
|
75
|
+
return null;
|
|
76
|
+
const b = body;
|
|
77
|
+
return b.details?.[0]?.issue ?? b.name ?? null;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* OAuth token cache — per (environment, clientId), refreshed 60s early.
|
|
81
|
+
* Module-level: serverless instances reuse it across warm invocations.
|
|
82
|
+
*/
|
|
83
|
+
const tokenCache = new Map();
|
|
84
|
+
async function getAccessToken(creds) {
|
|
85
|
+
const cacheKey = `${creds.environment}:${creds.clientId}`;
|
|
86
|
+
const cached = tokenCache.get(cacheKey);
|
|
87
|
+
if (cached && cached.expiresAt > Date.now())
|
|
88
|
+
return cached.token;
|
|
89
|
+
const res = await fetch(`${PAYPAL_HOSTS[creds.environment]}/v1/oauth2/token`, {
|
|
90
|
+
method: 'POST',
|
|
91
|
+
headers: {
|
|
92
|
+
Authorization: `Basic ${Buffer.from(`${creds.clientId}:${creds.clientSecret}`).toString('base64')}`,
|
|
93
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
94
|
+
},
|
|
95
|
+
body: 'grant_type=client_credentials',
|
|
96
|
+
});
|
|
97
|
+
const data = (await res.json().catch(() => null));
|
|
98
|
+
if (!res.ok || !data?.access_token) {
|
|
99
|
+
throw new PaypalApiError('PayPal authentication failed', res.status, data);
|
|
100
|
+
}
|
|
101
|
+
tokenCache.set(cacheKey, {
|
|
102
|
+
token: data.access_token,
|
|
103
|
+
expiresAt: Date.now() + Math.max(0, (data.expires_in ?? 0) - 60) * 1000,
|
|
104
|
+
});
|
|
105
|
+
return data.access_token;
|
|
106
|
+
}
|
|
107
|
+
async function paypalRequest(creds, method, path, body, headers) {
|
|
108
|
+
const token = await getAccessToken(creds);
|
|
109
|
+
const res = await fetch(`${PAYPAL_HOSTS[creds.environment]}${path}`, {
|
|
110
|
+
method,
|
|
111
|
+
headers: {
|
|
112
|
+
Authorization: `Bearer ${token}`,
|
|
113
|
+
'Content-Type': 'application/json',
|
|
114
|
+
...headers,
|
|
115
|
+
},
|
|
116
|
+
body: body === undefined ? undefined : JSON.stringify(body),
|
|
117
|
+
});
|
|
118
|
+
const data = (await res.json().catch(() => null));
|
|
119
|
+
if (!res.ok) {
|
|
120
|
+
throw new PaypalApiError(`PayPal ${method} ${path} failed (${res.status})`, res.status, data);
|
|
121
|
+
}
|
|
122
|
+
return data;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Credential probe used by the admin Payments panel's validate-before-persist
|
|
126
|
+
* (an OAuth token grant is the cheapest authenticated call). Returns an error
|
|
127
|
+
* message or null when the credentials are accepted.
|
|
128
|
+
*/
|
|
129
|
+
export async function validatePaypalCredentials(creds) {
|
|
130
|
+
try {
|
|
131
|
+
const res = await fetch(`${PAYPAL_HOSTS[creds.environment]}/v1/oauth2/token`, {
|
|
132
|
+
method: 'POST',
|
|
133
|
+
headers: {
|
|
134
|
+
Authorization: `Basic ${Buffer.from(`${creds.clientId}:${creds.clientSecret}`).toString('base64')}`,
|
|
135
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
136
|
+
},
|
|
137
|
+
body: 'grant_type=client_credentials',
|
|
138
|
+
});
|
|
139
|
+
if (res.ok)
|
|
140
|
+
return null;
|
|
141
|
+
if (res.status === 401 || res.status === 400) {
|
|
142
|
+
return 'PayPal rejected these credentials. Check the Client ID, secret and environment.';
|
|
143
|
+
}
|
|
144
|
+
return `PayPal returned an unexpected error (${res.status}). Try again shortly.`;
|
|
145
|
+
}
|
|
146
|
+
catch {
|
|
147
|
+
return 'Could not reach PayPal to verify the credentials. Try again shortly.';
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
export async function createPaypalOrder(input) {
|
|
151
|
+
const value = (input.totalCents / 100).toFixed(2);
|
|
152
|
+
const address = input.shippingAddress;
|
|
153
|
+
const hasAddress = !!(address?.line1 && address?.city && address?.country);
|
|
154
|
+
const data = await paypalRequest(input.credentials, 'POST', '/v2/checkout/orders', {
|
|
155
|
+
intent: 'CAPTURE',
|
|
156
|
+
purchase_units: [
|
|
157
|
+
{
|
|
158
|
+
reference_id: 'default',
|
|
159
|
+
invoice_id: input.reference,
|
|
160
|
+
custom_id: input.reference,
|
|
161
|
+
amount: { currency_code: input.currency.toUpperCase(), value },
|
|
162
|
+
...(hasAddress
|
|
163
|
+
? {
|
|
164
|
+
shipping: {
|
|
165
|
+
name: {
|
|
166
|
+
full_name: `${address.firstName ?? ''} ${address.lastName ?? ''}`.trim() || undefined,
|
|
167
|
+
},
|
|
168
|
+
address: {
|
|
169
|
+
address_line_1: address.line1,
|
|
170
|
+
...(address.line2 ? { address_line_2: address.line2 } : {}),
|
|
171
|
+
admin_area_2: address.city,
|
|
172
|
+
...(address.state ? { admin_area_1: address.state } : {}),
|
|
173
|
+
...(address.postalCode ? { postal_code: address.postalCode } : {}),
|
|
174
|
+
country_code: address.country.toUpperCase(),
|
|
175
|
+
},
|
|
176
|
+
},
|
|
177
|
+
}
|
|
178
|
+
: {}),
|
|
179
|
+
},
|
|
180
|
+
],
|
|
181
|
+
payment_source: {
|
|
182
|
+
paypal: {
|
|
183
|
+
experience_context: {
|
|
184
|
+
brand_name: input.storeName.slice(0, 127),
|
|
185
|
+
user_action: 'PAY_NOW',
|
|
186
|
+
// No eChecks/pending funding — a capture must settle or fail.
|
|
187
|
+
payment_method_preference: 'IMMEDIATE_PAYMENT_REQUIRED',
|
|
188
|
+
// The buyer cannot swap the address at PayPal — ours is the one
|
|
189
|
+
// the quote taxed and priced shipping for.
|
|
190
|
+
shipping_preference: hasAddress ? 'SET_PROVIDED_ADDRESS' : 'NO_SHIPPING',
|
|
191
|
+
return_url: input.returnUrl,
|
|
192
|
+
cancel_url: input.cancelUrl,
|
|
193
|
+
...(input.locale === 'fr' ? { locale: 'fr-FR' } : {}),
|
|
194
|
+
},
|
|
195
|
+
},
|
|
196
|
+
},
|
|
197
|
+
}, { 'PayPal-Request-Id': input.reference, Prefer: 'return=representation' });
|
|
198
|
+
if (!data.id) {
|
|
199
|
+
throw new PaypalApiError('PayPal order creation returned no id', 502, data);
|
|
200
|
+
}
|
|
201
|
+
const approve = data.links?.find((l) => l.rel === 'payer-action') ??
|
|
202
|
+
data.links?.find((l) => l.rel === 'approve');
|
|
203
|
+
return { paypalOrderId: data.id, approveUrl: approve?.href ?? null };
|
|
204
|
+
}
|
|
205
|
+
function extractCapture(data) {
|
|
206
|
+
const unit = data.purchase_units?.[0];
|
|
207
|
+
const capture = unit?.payments?.captures?.[0];
|
|
208
|
+
const amountValue = capture?.amount?.value;
|
|
209
|
+
return {
|
|
210
|
+
status: capture?.status ?? data.status ?? 'UNKNOWN',
|
|
211
|
+
captureId: capture?.id ?? null,
|
|
212
|
+
invoiceId: capture?.invoice_id ?? capture?.custom_id ?? unit?.invoice_id ?? unit?.custom_id ?? null,
|
|
213
|
+
amountCents: amountValue != null ? Math.round(parseFloat(amountValue) * 100) : null,
|
|
214
|
+
currency: capture?.amount?.currency_code ?? null,
|
|
215
|
+
payerEmail: data.payer?.email_address ?? null,
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Capture an approved PayPal order. Idempotent: a repeat capture (retry, or
|
|
220
|
+
* the return-leg racing the buttons leg) resolves via ORDER_ALREADY_CAPTURED
|
|
221
|
+
* → the order details are fetched and returned as a success.
|
|
222
|
+
*/
|
|
223
|
+
export async function capturePaypalOrder(credentials, paypalOrderId) {
|
|
224
|
+
try {
|
|
225
|
+
const data = await paypalRequest(credentials, 'POST', `/v2/checkout/orders/${encodeURIComponent(paypalOrderId)}/capture`, {}, { 'PayPal-Request-Id': `cap-${paypalOrderId}`, Prefer: 'return=representation' });
|
|
226
|
+
return extractCapture(data);
|
|
227
|
+
}
|
|
228
|
+
catch (err) {
|
|
229
|
+
if (err instanceof PaypalApiError && err.issue === 'ORDER_ALREADY_CAPTURED') {
|
|
230
|
+
const data = await paypalRequest(credentials, 'GET', `/v2/checkout/orders/${encodeURIComponent(paypalOrderId)}`);
|
|
231
|
+
return extractCapture(data);
|
|
232
|
+
}
|
|
233
|
+
throw err;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
export function readPaypalWebhookHeaders(headers) {
|
|
237
|
+
return {
|
|
238
|
+
transmissionId: headers.get('paypal-transmission-id'),
|
|
239
|
+
transmissionTime: headers.get('paypal-transmission-time'),
|
|
240
|
+
certUrl: headers.get('paypal-cert-url'),
|
|
241
|
+
authAlgo: headers.get('paypal-auth-algo'),
|
|
242
|
+
transmissionSig: headers.get('paypal-transmission-sig'),
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* Verify a webhook delivery via PayPal's verify-webhook-signature endpoint
|
|
247
|
+
* (requires the merchant's webhook_id — configured in the Payments panel).
|
|
248
|
+
*/
|
|
249
|
+
export async function verifyPaypalWebhook(credentials, webhookId, headers, event) {
|
|
250
|
+
if (!headers.transmissionId ||
|
|
251
|
+
!headers.transmissionTime ||
|
|
252
|
+
!headers.certUrl ||
|
|
253
|
+
!headers.authAlgo ||
|
|
254
|
+
!headers.transmissionSig) {
|
|
255
|
+
return false;
|
|
256
|
+
}
|
|
257
|
+
try {
|
|
258
|
+
const data = await paypalRequest(credentials, 'POST', '/v1/notifications/verify-webhook-signature', {
|
|
259
|
+
transmission_id: headers.transmissionId,
|
|
260
|
+
transmission_time: headers.transmissionTime,
|
|
261
|
+
cert_url: headers.certUrl,
|
|
262
|
+
auth_algo: headers.authAlgo,
|
|
263
|
+
transmission_sig: headers.transmissionSig,
|
|
264
|
+
webhook_id: webhookId,
|
|
265
|
+
webhook_event: event,
|
|
266
|
+
});
|
|
267
|
+
return data.verification_status === 'SUCCESS';
|
|
268
|
+
}
|
|
269
|
+
catch (err) {
|
|
270
|
+
console.error('[PayPal] webhook verification call failed:', err);
|
|
271
|
+
return false;
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
//# sourceMappingURL=paypal.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"paypal.js","sourceRoot":"","sources":["../../../src/checkout/server/paypal.ts"],"names":[],"mappings":"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"}
|