@alphawebai/hs-prism 0.1.0-rc.42 → 0.1.0-rc.44
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/src/alphaweb/contracts.d.ts +0 -1
- package/dist/src/alphaweb/contracts.js +1 -3
- package/dist/src/alphaweb/error.d.ts +0 -3
- package/dist/src/alphaweb/error.js +0 -3
- package/dist/src/alphaweb/manifest.d.ts +1 -3
- package/dist/src/alphaweb/manifest.js +1 -3
- package/dist/src/alphaweb/paypal-connector.d.ts +1 -8
- package/dist/src/alphaweb/paypal-connector.js +18 -216
- package/dist/src/alphaweb/stripe-connector.d.ts +1 -5
- package/dist/src/alphaweb/stripe-connector.js +2 -123
- package/dist/src/alphaweb/types.d.ts +7 -97
- package/dist/src/payments/generated/libconnector_service_ffi.so +0 -0
- package/package.json +1 -1
- package/dist/src/alphaweb/paypal-approved-order.d.ts +0 -49
- package/dist/src/alphaweb/paypal-approved-order.js +0 -326
|
@@ -3,5 +3,4 @@ export type { AlphaWebPrismCompatibilityManifest } from './manifest';
|
|
|
3
3
|
export { PrismOperationError, normalizePrismError } from './error';
|
|
4
4
|
export { assertSafeProviderMoney, fromProviderMinorAmount, getCurrencyRule, toProviderMinorAmount, } from './money';
|
|
5
5
|
export type { CurrencyRule } from './money';
|
|
6
|
-
export { paypalQuotesMatch } from './paypal-approved-order';
|
|
7
6
|
export type * from './types';
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.toProviderMinorAmount = exports.getCurrencyRule = exports.fromProviderMinorAmount = exports.assertSafeProviderMoney = exports.normalizePrismError = exports.PrismOperationError = exports.ALPHAWEB_PRISM_COMPATIBILITY_MANIFEST = void 0;
|
|
4
4
|
var manifest_1 = require("./manifest");
|
|
5
5
|
Object.defineProperty(exports, "ALPHAWEB_PRISM_COMPATIBILITY_MANIFEST", { enumerable: true, get: function () { return manifest_1.ALPHAWEB_PRISM_COMPATIBILITY_MANIFEST; } });
|
|
6
6
|
var error_1 = require("./error");
|
|
@@ -11,6 +11,4 @@ Object.defineProperty(exports, "assertSafeProviderMoney", { enumerable: true, ge
|
|
|
11
11
|
Object.defineProperty(exports, "fromProviderMinorAmount", { enumerable: true, get: function () { return money_1.fromProviderMinorAmount; } });
|
|
12
12
|
Object.defineProperty(exports, "getCurrencyRule", { enumerable: true, get: function () { return money_1.getCurrencyRule; } });
|
|
13
13
|
Object.defineProperty(exports, "toProviderMinorAmount", { enumerable: true, get: function () { return money_1.toProviderMinorAmount; } });
|
|
14
|
-
var paypal_approved_order_1 = require("./paypal-approved-order");
|
|
15
|
-
Object.defineProperty(exports, "paypalQuotesMatch", { enumerable: true, get: function () { return paypal_approved_order_1.paypalQuotesMatch; } });
|
|
16
14
|
//# sourceMappingURL=contracts.js.map
|
|
@@ -2,9 +2,6 @@ export interface PrismOperationErrorShape {
|
|
|
2
2
|
category: 'configuration' | 'authentication' | 'validation' | 'customer_action' | 'declined' | 'rate_limited' | 'network' | 'provider_server' | 'conflict' | 'unknown';
|
|
3
3
|
providerCode?: string;
|
|
4
4
|
providerRequestId?: string;
|
|
5
|
-
providerHttpStatus?: number;
|
|
6
|
-
/** Provider issue identifiers only; never descriptions, field values, or raw responses. */
|
|
7
|
-
providerIssueCodes?: readonly string[];
|
|
8
5
|
retryable: boolean;
|
|
9
6
|
outcome: 'definitely_not_applied' | 'applied' | 'ambiguous';
|
|
10
7
|
safeMessage: string;
|
|
@@ -101,9 +101,6 @@ function normalizePrismError(error) {
|
|
|
101
101
|
category,
|
|
102
102
|
...(code ? { providerCode: code } : {}),
|
|
103
103
|
...(requestId ? { providerRequestId: requestId } : {}),
|
|
104
|
-
...(Number.isInteger(status) && status >= 100 && status <= 599
|
|
105
|
-
? { providerHttpStatus: status }
|
|
106
|
-
: {}),
|
|
107
104
|
retryable,
|
|
108
105
|
outcome,
|
|
109
106
|
safeMessage,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export declare const ALPHAWEB_STRIPE_WEBHOOK_EVENTS: readonly ["charge.dispute.closed", "charge.dispute.created", "charge.dispute.funds_reinstated", "charge.dispute.funds_withdrawn", "charge.dispute.updated", "mandate.updated", "payment_intent.amount_capturable_updated", "payment_intent.canceled", "payment_intent.payment_failed", "payment_intent.processing", "payment_intent.requires_action", "payment_intent.succeeded", "payment_method.attached", "payment_method.automatically_updated", "payment_method.detached", "payment_method.updated", "refund.created", "refund.failed", "refund.updated", "setup_intent.canceled", "setup_intent.requires_action", "setup_intent.setup_failed", "setup_intent.succeeded"];
|
|
2
2
|
export declare const ALPHAWEB_PAYPAL_WEBHOOK_EVENTS: readonly ["CHECKOUT.ORDER.APPROVED", "CHECKOUT.ORDER.COMPLETED", "CHECKOUT.ORDER.PROCESSED", "CHECKOUT.PAYMENT-APPROVAL.REVERSED", "CUSTOMER.DISPUTE.CREATED", "CUSTOMER.DISPUTE.RESOLVED", "CUSTOMER.DISPUTE.UPDATED", "PAYMENT.AUTHORIZATION.CREATED", "PAYMENT.AUTHORIZATION.VOIDED", "PAYMENT.CAPTURE.COMPLETED", "PAYMENT.CAPTURE.DECLINED", "PAYMENT.CAPTURE.PENDING", "PAYMENT.CAPTURE.REFUNDED", "PAYMENT.CAPTURE.REVERSED", "PAYMENT.REFUND.FAILED", "PAYMENT.REFUND.PENDING", "RISK.DISPUTE.CREATED", "VAULT.PAYMENT-TOKEN.CREATED", "VAULT.PAYMENT-TOKEN.DELETED", "VAULT.PAYMENT-TOKEN.DELETION-INITIATED"];
|
|
3
3
|
export declare const ALPHAWEB_PRISM_COMPATIBILITY_MANIFEST: Readonly<{
|
|
4
|
-
readonly release: "0.1.0-rc.
|
|
4
|
+
readonly release: "0.1.0-rc.44";
|
|
5
5
|
readonly upstreamCommit: "b07af7d9dac271de729548e74217ddcf895c602b";
|
|
6
6
|
readonly medusaRange: ">=2.17.2 <2.18.0";
|
|
7
7
|
readonly stripeApiVersion: "2026-06-24.dahlia";
|
|
@@ -24,7 +24,6 @@ export declare const ALPHAWEB_PRISM_COMPATIBILITY_MANIFEST: Readonly<{
|
|
|
24
24
|
readonly connectorCapabilities: {
|
|
25
25
|
readonly stripe: {
|
|
26
26
|
readonly browserSession: "stripe_payment_element";
|
|
27
|
-
readonly manualAuthorizationCheckout: "v1";
|
|
28
27
|
readonly oneTimePayment: true;
|
|
29
28
|
readonly futureUse: true;
|
|
30
29
|
readonly repeatPayment: true;
|
|
@@ -35,7 +34,6 @@ export declare const ALPHAWEB_PRISM_COMPATIBILITY_MANIFEST: Readonly<{
|
|
|
35
34
|
};
|
|
36
35
|
readonly paypal: {
|
|
37
36
|
readonly browserSession: "paypal_checkout";
|
|
38
|
-
readonly approvedOrderCheckout: "v1";
|
|
39
37
|
readonly oneTimePayment: true;
|
|
40
38
|
readonly futureUse: true;
|
|
41
39
|
readonly repeatPayment: true;
|
|
@@ -49,7 +49,7 @@ exports.ALPHAWEB_PAYPAL_WEBHOOK_EVENTS = Object.freeze([
|
|
|
49
49
|
'VAULT.PAYMENT-TOKEN.DELETION-INITIATED',
|
|
50
50
|
]);
|
|
51
51
|
exports.ALPHAWEB_PRISM_COMPATIBILITY_MANIFEST = Object.freeze({
|
|
52
|
-
release: '0.1.0-rc.
|
|
52
|
+
release: '0.1.0-rc.44',
|
|
53
53
|
upstreamCommit: 'b07af7d9dac271de729548e74217ddcf895c602b',
|
|
54
54
|
medusaRange: '>=2.17.2 <2.18.0',
|
|
55
55
|
stripeApiVersion: '2026-06-24.dahlia',
|
|
@@ -72,7 +72,6 @@ exports.ALPHAWEB_PRISM_COMPATIBILITY_MANIFEST = Object.freeze({
|
|
|
72
72
|
connectorCapabilities: {
|
|
73
73
|
stripe: {
|
|
74
74
|
browserSession: 'stripe_payment_element',
|
|
75
|
-
manualAuthorizationCheckout: 'v1',
|
|
76
75
|
oneTimePayment: true,
|
|
77
76
|
futureUse: true,
|
|
78
77
|
repeatPayment: true,
|
|
@@ -83,7 +82,6 @@ exports.ALPHAWEB_PRISM_COMPATIBILITY_MANIFEST = Object.freeze({
|
|
|
83
82
|
},
|
|
84
83
|
paypal: {
|
|
85
84
|
browserSession: 'paypal_checkout',
|
|
86
|
-
approvedOrderCheckout: 'v1',
|
|
87
85
|
oneTimePayment: true,
|
|
88
86
|
futureUse: true,
|
|
89
87
|
repeatPayment: true,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { types } from '../payments/generated/proto';
|
|
2
|
-
import type { PrismAuthorizeApprovedPaymentInput, PrismCancelInput, PrismCaptureInput, PrismCreateWebhookEndpointInput, PrismDeleteWebhookEndpointInput, PrismDisputeResult, PrismEstablishReusableReferenceInput, PrismFindPaymentInput, PrismFindRefundInput, PrismMerchantOperation, PrismPayPalConnectorOptions, PrismPayPalConnectorV1,
|
|
2
|
+
import type { PrismAuthorizeApprovedPaymentInput, PrismCancelInput, PrismCaptureInput, PrismCreateWebhookEndpointInput, PrismDeleteWebhookEndpointInput, PrismDisputeResult, PrismEstablishReusableReferenceInput, PrismFindPaymentInput, PrismFindRefundInput, PrismMerchantOperation, PrismPayPalConnectorOptions, PrismPayPalConnectorV1, PrismPayPalOrderLifecycle, PrismPayPalRuntimeVerification, PrismPayPalRuntimeVerificationInput, PrismPaymentResult, PrismRefundInput, PrismRefundResult, PrismRetrieveDisputeInput, PrismRetrieveOrderLifecycleInput, PrismRetrievePaymentInput, PrismRetrieveRefundInput, PrismRetrieveWebhookEndpointInput, PrismRetryPaymentInput, PrismReusableReferenceDerivation, PrismUpdateWebhookEndpointInput, PrismWebhookEndpoint, PrismWebhookEndpointCreated, PrismWebhookParseInput, PrismWebhookParseResult } from './types';
|
|
3
3
|
interface MerchantAuthenticationPort {
|
|
4
4
|
createServerAuthenticationToken(input: types.IMerchantAuthenticationServiceCreateServerAuthenticationTokenRequest): Promise<types.MerchantAuthenticationServiceCreateServerAuthenticationTokenResponse>;
|
|
5
5
|
}
|
|
@@ -32,16 +32,9 @@ export declare class PrismPayPalConnector implements PrismPayPalConnectorV1 {
|
|
|
32
32
|
private validateOptions;
|
|
33
33
|
private assertOperation;
|
|
34
34
|
private connectorState;
|
|
35
|
-
private paypalResponse;
|
|
36
35
|
private paypalRequest;
|
|
37
36
|
private retrievePayPalVaultState;
|
|
38
37
|
createPayment(input: PrismMerchantOperation): Promise<PrismPaymentResult>;
|
|
39
|
-
createAdjustableOrder(input: PrismMerchantOperation): Promise<PrismPaymentResult>;
|
|
40
|
-
private createPayPalOrder;
|
|
41
|
-
private assertOrderOwner;
|
|
42
|
-
retrieveApprovedOrder(input: PrismPayPalOrderOwner): Promise<PrismPayPalApprovedOrder>;
|
|
43
|
-
updateApprovedOrder(input: PrismPayPalUpdateApprovedOrderInput): Promise<PrismPayPalApprovedOrder>;
|
|
44
|
-
captureApprovedOrder(input: PrismPayPalCaptureApprovedOrderInput): Promise<PrismPayPalApprovedOrder>;
|
|
45
38
|
authorizeApprovedPayment(input: PrismAuthorizeApprovedPaymentInput): Promise<PrismPaymentResult>;
|
|
46
39
|
repeatPayment(input: PrismMerchantOperation): Promise<PrismPaymentResult>;
|
|
47
40
|
retryPayment(input: PrismRetryPaymentInput): Promise<PrismPaymentResult>;
|
|
@@ -8,14 +8,6 @@ const proto_1 = require("../payments/generated/proto");
|
|
|
8
8
|
const error_1 = require("./error");
|
|
9
9
|
const manifest_1 = require("./manifest");
|
|
10
10
|
const money_1 = require("./money");
|
|
11
|
-
const paypal_approved_order_1 = require("./paypal-approved-order");
|
|
12
|
-
function safeProviderIdentifier(value) {
|
|
13
|
-
if (typeof value !== 'string' || value.length === 0 || value.length > 127)
|
|
14
|
-
return undefined;
|
|
15
|
-
return [...value].every((character) => (character >= 'a' && character <= 'z') ||
|
|
16
|
-
(character >= 'A' && character <= 'Z') ||
|
|
17
|
-
(character >= '0' && character <= '9') || character === '_' || character === '-') ? value : undefined;
|
|
18
|
-
}
|
|
19
11
|
const PAYPAL_API_BASE = {
|
|
20
12
|
test: 'https://api-m.sandbox.paypal.com',
|
|
21
13
|
live: 'https://api-m.paypal.com',
|
|
@@ -143,6 +135,9 @@ function browserContinuation(input) {
|
|
|
143
135
|
return { returnUrl: returnUrl.toString(), cancelUrl: cancelUrl.toString() };
|
|
144
136
|
}
|
|
145
137
|
function futureUseMitCategoryFromIntent(futureUse) {
|
|
138
|
+
if (futureUse?.recurringAgreements) {
|
|
139
|
+
throw validationError('PayPal requires its supported single recurring agreement.', 'PRISM_INDEPENDENT_RECURRING_AGREEMENTS_UNSUPPORTED');
|
|
140
|
+
}
|
|
146
141
|
if (futureUse?.recurringAgreement?.totalPayments !== null &&
|
|
147
142
|
futureUse?.recurringAgreement?.totalPayments !== undefined) {
|
|
148
143
|
return proto_1.types.MitCategory.INSTALLMENT_MIT;
|
|
@@ -261,13 +256,11 @@ function connectorMetadataFrom(response) {
|
|
|
261
256
|
}
|
|
262
257
|
function failureFrom(response) {
|
|
263
258
|
const error = recordOf(recordOf(response).error);
|
|
264
|
-
const code =
|
|
265
|
-
safeProviderIdentifier(error.code);
|
|
259
|
+
const code = stringOf(error.code);
|
|
266
260
|
if (!code && Object.keys(error).length === 0)
|
|
267
261
|
return undefined;
|
|
268
262
|
return {
|
|
269
|
-
category:
|
|
270
|
-
code === 'PAYER_ACTION_REQUIRED' ? 'customer_action' : 'unknown',
|
|
263
|
+
category: 'unknown',
|
|
271
264
|
...(code ? { providerCode: code } : {}),
|
|
272
265
|
retryable: false,
|
|
273
266
|
};
|
|
@@ -373,10 +366,6 @@ function paymentResult(response, money, options, fallbackConnectorPaymentId, app
|
|
|
373
366
|
const connectorPaymentId = connectorPaymentIdFrom(response) ?? fallbackConnectorPaymentId ?? null;
|
|
374
367
|
const connectorMetadata = connectorMetadataFrom(response);
|
|
375
368
|
const failure = failureFrom(response);
|
|
376
|
-
const responseData = recordOf(response);
|
|
377
|
-
const providerRequestId = safeProviderIdentifier(Object.entries(recordOf(responseData.responseHeaders))
|
|
378
|
-
.find(([key]) => key.toLowerCase() === 'paypal-debug-id')?.[1]);
|
|
379
|
-
const providerHttpStatus = Number(responseData.statusCode);
|
|
380
369
|
const providerReference = connectorPaymentId
|
|
381
370
|
? reusableReferenceFrom(response, options, connectorPaymentId)
|
|
382
371
|
: undefined;
|
|
@@ -390,9 +379,6 @@ function paymentResult(response, money, options, fallbackConnectorPaymentId, app
|
|
|
390
379
|
...(connectorMetadata ? { connectorMetadata } : {}),
|
|
391
380
|
...(reusableReference ? { reusableReference } : {}),
|
|
392
381
|
...(failure ? { failure } : {}),
|
|
393
|
-
...(providerRequestId ? { providerRequestId } : {}),
|
|
394
|
-
...(Number.isInteger(providerHttpStatus) && providerHttpStatus >= 100 && providerHttpStatus <= 599
|
|
395
|
-
? { providerHttpStatus } : {}),
|
|
396
382
|
};
|
|
397
383
|
}
|
|
398
384
|
function refundStatus(status) {
|
|
@@ -774,7 +760,7 @@ class PrismPayPalConnector {
|
|
|
774
760
|
},
|
|
775
761
|
};
|
|
776
762
|
}
|
|
777
|
-
async
|
|
763
|
+
async paypalRequest(method, path, body, idempotencyKey) {
|
|
778
764
|
const state = await this.connectorState();
|
|
779
765
|
const accessToken = readSecret(state.accessToken?.token);
|
|
780
766
|
if (!accessToken) {
|
|
@@ -793,7 +779,7 @@ class PrismPayPalConnector {
|
|
|
793
779
|
...(body === undefined ? {} : { body: JSON.stringify(body) }),
|
|
794
780
|
signal: controller.signal,
|
|
795
781
|
});
|
|
796
|
-
const requestId =
|
|
782
|
+
const requestId = response.headers.get('paypal-debug-id') ?? undefined;
|
|
797
783
|
const text = await response.text();
|
|
798
784
|
let payload = null;
|
|
799
785
|
if (text.length > 0) {
|
|
@@ -806,37 +792,22 @@ class PrismPayPalConnector {
|
|
|
806
792
|
}
|
|
807
793
|
if (!response.ok) {
|
|
808
794
|
const data = recordOf(payload);
|
|
809
|
-
const issues = (Array.isArray(data.details) ? data.details : [])
|
|
810
|
-
.map((detail) => safeProviderIdentifier(recordOf(detail).issue))
|
|
811
|
-
.filter((issue) => issue !== undefined)
|
|
812
|
-
.slice(0, 10);
|
|
813
|
-
const providerCode = safeProviderIdentifier(data.name);
|
|
814
|
-
const providerRequestId = requestId ?? safeProviderIdentifier(data.debug_id);
|
|
815
795
|
throw new error_1.PrismOperationError({
|
|
816
796
|
category: response.status === 401 || response.status === 403
|
|
817
797
|
? 'authentication'
|
|
818
|
-
: response.status ===
|
|
819
|
-
? '
|
|
820
|
-
: response.status
|
|
821
|
-
? '
|
|
822
|
-
:
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
providerHttpStatus: response.status,
|
|
828
|
-
...(issues.length ? { providerIssueCodes: [...new Set(issues)] } : {}),
|
|
829
|
-
retryable: response.status === 409 || response.status === 429 || response.status >= 500,
|
|
830
|
-
outcome: response.status === 409 || response.status >= 500
|
|
831
|
-
? 'ambiguous' : 'definitely_not_applied',
|
|
798
|
+
: response.status === 429
|
|
799
|
+
? 'rate_limited'
|
|
800
|
+
: response.status >= 500
|
|
801
|
+
? 'provider_server'
|
|
802
|
+
: 'validation',
|
|
803
|
+
...(stringOf(data.name) ? { providerCode: stringOf(data.name) } : {}),
|
|
804
|
+
...(requestId ? { providerRequestId: requestId } : {}),
|
|
805
|
+
retryable: response.status === 429 || response.status >= 500,
|
|
806
|
+
outcome: response.status >= 500 ? 'ambiguous' : 'definitely_not_applied',
|
|
832
807
|
safeMessage: 'PayPal rejected the payment operation.',
|
|
833
808
|
});
|
|
834
809
|
}
|
|
835
|
-
return
|
|
836
|
-
payload,
|
|
837
|
-
providerHttpStatus: response.status,
|
|
838
|
-
...(requestId ? { providerRequestId: requestId } : {}),
|
|
839
|
-
};
|
|
810
|
+
return payload;
|
|
840
811
|
}
|
|
841
812
|
catch (error) {
|
|
842
813
|
throw (0, error_1.normalizePrismError)(error);
|
|
@@ -845,23 +816,11 @@ class PrismPayPalConnector {
|
|
|
845
816
|
clearTimeout(timeout);
|
|
846
817
|
}
|
|
847
818
|
}
|
|
848
|
-
async paypalRequest(method, path, body, idempotencyKey) {
|
|
849
|
-
return (await this.paypalResponse(method, path, body, idempotencyKey)).payload;
|
|
850
|
-
}
|
|
851
819
|
async retrievePayPalVaultState(connectorPaymentId) {
|
|
852
820
|
const payload = await this.paypalRequest('GET', `/v2/checkout/orders/${encodeURIComponent(connectorPaymentId)}?fields=payment_source`);
|
|
853
821
|
return paypalVaultStateFromOrder(payload, this.options, connectorPaymentId);
|
|
854
822
|
}
|
|
855
823
|
async createPayment(input) {
|
|
856
|
-
return this.createPayPalOrder(input, true);
|
|
857
|
-
}
|
|
858
|
-
async createAdjustableOrder(input) {
|
|
859
|
-
if (input.metadata.alphaweb_capture_method === 'manual') {
|
|
860
|
-
throw validationError('An adjustable PayPal order must use CAPTURE intent.', 'PRISM_PAYPAL_ORDER_INTENT');
|
|
861
|
-
}
|
|
862
|
-
return this.createPayPalOrder(input, false);
|
|
863
|
-
}
|
|
864
|
-
async createPayPalOrder(input, commit) {
|
|
865
824
|
this.assertOperation(input);
|
|
866
825
|
if (input.methodSelection.kind !== 'interactive_dynamic' || input.reusableReference) {
|
|
867
826
|
throw validationError('Interactive payment input is invalid.', 'PRISM_INTERACTIVE_INPUT');
|
|
@@ -877,10 +836,7 @@ class PrismPayPalConnector {
|
|
|
877
836
|
const order = await this.dependencies.payments.createOrder({
|
|
878
837
|
merchantOrderId: input.operationId,
|
|
879
838
|
amount: moneyMessage(input.money),
|
|
880
|
-
metadata: secret(JSON.stringify(
|
|
881
|
-
...operationMetadata(input),
|
|
882
|
-
...(!commit ? { alphaweb_paypal_commit: false } : {}),
|
|
883
|
-
})),
|
|
839
|
+
metadata: secret(JSON.stringify(operationMetadata(input))),
|
|
884
840
|
state,
|
|
885
841
|
testMode: this.options.environment === 'test',
|
|
886
842
|
paymentMethodType: proto_1.types.PaymentMethodType.PAY_PAL,
|
|
@@ -896,18 +852,6 @@ class PrismPayPalConnector {
|
|
|
896
852
|
});
|
|
897
853
|
const orderId = stringOf(order.connectorOrderId);
|
|
898
854
|
if (!orderId) {
|
|
899
|
-
if (!commit) {
|
|
900
|
-
const result = paymentResult(order, input.money, this.options);
|
|
901
|
-
throw new error_1.PrismOperationError({
|
|
902
|
-
category: 'unknown',
|
|
903
|
-
providerCode: result.failure?.providerCode ?? 'PRISM_PAYPAL_ORDER_CREATE_UNCONFIRMED',
|
|
904
|
-
...(result.providerRequestId ? { providerRequestId: result.providerRequestId } : {}),
|
|
905
|
-
...(result.providerHttpStatus ? { providerHttpStatus: result.providerHttpStatus } : {}),
|
|
906
|
-
outcome: 'ambiguous',
|
|
907
|
-
retryable: false,
|
|
908
|
-
safeMessage: 'The PayPal order creation result must be verified before continuing.',
|
|
909
|
-
});
|
|
910
|
-
}
|
|
911
855
|
throw validationError('Prism did not return a PayPal order ID.', 'PRISM_PAYPAL_ORDER');
|
|
912
856
|
}
|
|
913
857
|
return {
|
|
@@ -922,7 +866,6 @@ class PrismPayPalConnector {
|
|
|
922
866
|
clientId: this.options.clientId,
|
|
923
867
|
environment: this.options.environment === 'test' ? 'sandbox' : 'production',
|
|
924
868
|
savePayment: setupFutureUsage,
|
|
925
|
-
...(!commit ? { commit: false } : {}),
|
|
926
869
|
},
|
|
927
870
|
};
|
|
928
871
|
}
|
|
@@ -930,147 +873,6 @@ class PrismPayPalConnector {
|
|
|
930
873
|
throw (0, error_1.normalizePrismError)(error);
|
|
931
874
|
}
|
|
932
875
|
}
|
|
933
|
-
assertOrderOwner(input) {
|
|
934
|
-
assertIdentifier(input.rootOperationId, 'rootOperationId');
|
|
935
|
-
assertIdentifier(input.connectorPaymentId, 'connectorPaymentId');
|
|
936
|
-
if (input.credentialBindingId !== this.options.credentialBindingId ||
|
|
937
|
-
input.providerAccountId !== this.options.providerAccountId ||
|
|
938
|
-
input.livemode !== (this.options.environment === 'live')) {
|
|
939
|
-
throw (0, paypal_approved_order_1.approvedOrderError)('PRISM_PAYPAL_ORDER_OWNER', 'definitely_not_applied');
|
|
940
|
-
}
|
|
941
|
-
}
|
|
942
|
-
async retrieveApprovedOrder(input) {
|
|
943
|
-
this.assertOrderOwner(input);
|
|
944
|
-
const response = await this.paypalResponse('GET', `/v2/checkout/orders/${encodeURIComponent(input.connectorPaymentId)}`);
|
|
945
|
-
try {
|
|
946
|
-
return {
|
|
947
|
-
...(0, paypal_approved_order_1.readApprovedOrder)(response.payload, input, this.options),
|
|
948
|
-
...(response.providerRequestId ? { providerRequestId: response.providerRequestId } : {}),
|
|
949
|
-
};
|
|
950
|
-
}
|
|
951
|
-
catch (error) {
|
|
952
|
-
const normalized = error instanceof error_1.PrismOperationError
|
|
953
|
-
? error : (0, paypal_approved_order_1.approvedOrderError)('PRISM_PAYPAL_ORDER_RESPONSE');
|
|
954
|
-
throw new error_1.PrismOperationError({
|
|
955
|
-
...normalized.shape,
|
|
956
|
-
outcome: 'ambiguous',
|
|
957
|
-
providerHttpStatus: response.providerHttpStatus,
|
|
958
|
-
...(response.providerRequestId ? { providerRequestId: response.providerRequestId } : {}),
|
|
959
|
-
});
|
|
960
|
-
}
|
|
961
|
-
}
|
|
962
|
-
async updateApprovedOrder(input) {
|
|
963
|
-
this.assertOrderOwner(input);
|
|
964
|
-
const payload = (0, paypal_approved_order_1.paypalQuotePayload)(input.quote);
|
|
965
|
-
(0, paypal_approved_order_1.assertRecurringDisclosurePreserved)(input.expectedQuote, input.quote);
|
|
966
|
-
const current = await this.retrieveApprovedOrder(input);
|
|
967
|
-
if (current.hasCapture || current.hasAuthorization ||
|
|
968
|
-
(current.orderStatus !== 'CREATED' && current.orderStatus !== 'APPROVED')) {
|
|
969
|
-
throw (0, paypal_approved_order_1.approvedOrderError)('PRISM_PAYPAL_ORDER_NOT_EDITABLE', 'definitely_not_applied');
|
|
970
|
-
}
|
|
971
|
-
if ((0, paypal_approved_order_1.paypalQuotesMatch)(current.quote, input.quote))
|
|
972
|
-
return current;
|
|
973
|
-
if (!(0, paypal_approved_order_1.paypalQuotesMatch)(current.quote, input.expectedQuote) ||
|
|
974
|
-
current.quote.money.currency !== input.quote.money.currency.toLowerCase()) {
|
|
975
|
-
throw (0, paypal_approved_order_1.approvedOrderError)('PRISM_PAYPAL_ORDER_QUOTE_CHANGED', 'definitely_not_applied');
|
|
976
|
-
}
|
|
977
|
-
const base = `/purchase_units/@reference_id=='${input.rootOperationId}'`;
|
|
978
|
-
const patches = [
|
|
979
|
-
{ op: 'replace', path: `${base}/amount`, value: payload.amount },
|
|
980
|
-
];
|
|
981
|
-
if (payload.items)
|
|
982
|
-
patches.push({ op: 'add', path: `${base}/items`, value: payload.items });
|
|
983
|
-
else if (current.quote.items)
|
|
984
|
-
patches.push({ op: 'remove', path: `${base}/items` });
|
|
985
|
-
// PATCH has no promised provider idempotency. The caller owns serialization;
|
|
986
|
-
// after an uncertain result it must retrieve this order before any next quote.
|
|
987
|
-
const response = await this.paypalResponse('PATCH', `/v2/checkout/orders/${encodeURIComponent(input.connectorPaymentId)}`, patches);
|
|
988
|
-
let updated;
|
|
989
|
-
try {
|
|
990
|
-
updated = await this.retrieveApprovedOrder(input);
|
|
991
|
-
}
|
|
992
|
-
catch (error) {
|
|
993
|
-
// A failed read after an acknowledged PATCH is not a rejected PATCH.
|
|
994
|
-
throw new error_1.PrismOperationError({
|
|
995
|
-
...(0, error_1.normalizePrismError)(error).shape,
|
|
996
|
-
outcome: 'ambiguous',
|
|
997
|
-
...(response.providerRequestId ? { providerRequestId: response.providerRequestId } : {}),
|
|
998
|
-
});
|
|
999
|
-
}
|
|
1000
|
-
if (!(0, paypal_approved_order_1.paypalQuotesMatch)(updated.quote, input.quote) || updated.hasCapture || updated.hasAuthorization) {
|
|
1001
|
-
throw new error_1.PrismOperationError({
|
|
1002
|
-
...(0, paypal_approved_order_1.approvedOrderError)('PRISM_PAYPAL_ORDER_UPDATE_UNCONFIRMED').shape,
|
|
1003
|
-
...(response.providerRequestId ? { providerRequestId: response.providerRequestId } : {}),
|
|
1004
|
-
});
|
|
1005
|
-
}
|
|
1006
|
-
return {
|
|
1007
|
-
...updated,
|
|
1008
|
-
...(response.providerRequestId ? { providerRequestId: response.providerRequestId } : {}),
|
|
1009
|
-
};
|
|
1010
|
-
}
|
|
1011
|
-
async captureApprovedOrder(input) {
|
|
1012
|
-
this.assertOrderOwner(input);
|
|
1013
|
-
assertIdentifier(input.idempotencyKey, 'idempotencyKey');
|
|
1014
|
-
const current = await this.retrieveApprovedOrder(input);
|
|
1015
|
-
if (!(0, paypal_approved_order_1.paypalQuotesMatch)(current.quote, input.quote)) {
|
|
1016
|
-
throw (0, paypal_approved_order_1.approvedOrderError)('PRISM_PAYPAL_ORDER_QUOTE_CHANGED', 'definitely_not_applied');
|
|
1017
|
-
}
|
|
1018
|
-
// A pending capture also owns the payment. Never create a second capture.
|
|
1019
|
-
if (current.hasCapture)
|
|
1020
|
-
return current;
|
|
1021
|
-
if (current.hasAuthorization || current.orderStatus !== 'APPROVED') {
|
|
1022
|
-
throw (0, paypal_approved_order_1.approvedOrderError)('PRISM_PAYPAL_ORDER_NOT_APPROVED', 'definitely_not_applied');
|
|
1023
|
-
}
|
|
1024
|
-
const providerKey = (0, node_crypto_1.createHash)('sha256')
|
|
1025
|
-
.update(JSON.stringify([input.credentialBindingId, input.connectorPaymentId, input.idempotencyKey]))
|
|
1026
|
-
.digest('hex')
|
|
1027
|
-
.slice(0, 32);
|
|
1028
|
-
const result = await this.authorizeApprovedPayment({
|
|
1029
|
-
connectorPaymentId: input.connectorPaymentId,
|
|
1030
|
-
operationId: input.rootOperationId,
|
|
1031
|
-
idempotencyKey: providerKey,
|
|
1032
|
-
money: input.quote.money,
|
|
1033
|
-
captureMethod: 'automatic',
|
|
1034
|
-
});
|
|
1035
|
-
let captured;
|
|
1036
|
-
try {
|
|
1037
|
-
captured = await this.retrieveApprovedOrder(input);
|
|
1038
|
-
}
|
|
1039
|
-
catch (error) {
|
|
1040
|
-
// Capture may already have succeeded; a later GET failure cannot undo it.
|
|
1041
|
-
throw new error_1.PrismOperationError({
|
|
1042
|
-
...(0, error_1.normalizePrismError)(error).shape,
|
|
1043
|
-
outcome: 'ambiguous',
|
|
1044
|
-
...(result.providerRequestId ? { providerRequestId: result.providerRequestId } : {}),
|
|
1045
|
-
});
|
|
1046
|
-
}
|
|
1047
|
-
if (!(0, paypal_approved_order_1.paypalQuotesMatch)(captured.quote, input.quote)) {
|
|
1048
|
-
throw (0, paypal_approved_order_1.approvedOrderError)('PRISM_PAYPAL_ORDER_QUOTE_CHANGED');
|
|
1049
|
-
}
|
|
1050
|
-
if (!captured.capture) {
|
|
1051
|
-
if (result.status === 'failed' || result.status === 'requires_payment_method' ||
|
|
1052
|
-
result.status === 'requires_customer_action') {
|
|
1053
|
-
const category = result.status === 'requires_customer_action'
|
|
1054
|
-
? 'customer_action' : result.failure?.category ?? 'unknown';
|
|
1055
|
-
throw new error_1.PrismOperationError({
|
|
1056
|
-
category: category === 'invalid_payment_method' ? 'validation' : category,
|
|
1057
|
-
providerCode: result.failure?.providerCode ?? 'PRISM_PAYPAL_CAPTURE_NOT_COMPLETED',
|
|
1058
|
-
...(result.providerRequestId ? { providerRequestId: result.providerRequestId } : {}),
|
|
1059
|
-
...(result.providerHttpStatus ? { providerHttpStatus: result.providerHttpStatus } : {}),
|
|
1060
|
-
retryable: false,
|
|
1061
|
-
outcome: category === 'unknown' ? 'ambiguous' : 'definitely_not_applied',
|
|
1062
|
-
safeMessage: result.status === 'requires_customer_action'
|
|
1063
|
-
? 'PayPal requires customer action to complete this payment.'
|
|
1064
|
-
: 'PayPal did not complete this payment.',
|
|
1065
|
-
});
|
|
1066
|
-
}
|
|
1067
|
-
throw (0, paypal_approved_order_1.approvedOrderError)('PRISM_PAYPAL_CAPTURE_UNCONFIRMED');
|
|
1068
|
-
}
|
|
1069
|
-
return {
|
|
1070
|
-
...captured,
|
|
1071
|
-
...(result.providerRequestId ? { providerRequestId: result.providerRequestId } : {}),
|
|
1072
|
-
};
|
|
1073
|
-
}
|
|
1074
876
|
async authorizeApprovedPayment(input) {
|
|
1075
877
|
assertIdentifier(input.operationId, 'operationId');
|
|
1076
878
|
assertIdentifier(input.idempotencyKey, 'idempotencyKey');
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import { types } from '../payments/generated/proto';
|
|
2
|
-
import type { PrismCancelInput, PrismCaptureInput, PrismCreateWebhookEndpointInput, PrismDeleteWebhookEndpointInput, PrismDisputeResult, PrismEstablishReusableReferenceInput, PrismFindPaymentInput, PrismFindRefundInput, PrismMerchantOperation, PrismPaymentResult, PrismRefundInput, PrismRefundResult, PrismRetrieveDisputeInput, PrismRetrieveRefundInput, PrismRetryPaymentInput, PrismReusableReferenceDerivation, PrismRetrieveWebhookEndpointInput, PrismRetrievePaymentInput, PrismStripeConnectorOptions, PrismStripeConnectorV1,
|
|
2
|
+
import type { PrismCancelInput, PrismCaptureInput, PrismCreateWebhookEndpointInput, PrismDeleteWebhookEndpointInput, PrismDisputeResult, PrismEstablishReusableReferenceInput, PrismFindPaymentInput, PrismFindRefundInput, PrismMerchantOperation, PrismPaymentResult, PrismRefundInput, PrismRefundResult, PrismRetrieveDisputeInput, PrismRetrieveRefundInput, PrismRetryPaymentInput, PrismReusableReferenceDerivation, PrismRetrieveWebhookEndpointInput, PrismRetrievePaymentInput, PrismStripeConnectorOptions, PrismStripeConnectorV1, PrismStripeRuntimeVerification, PrismStripeRuntimeVerificationInput, PrismUpdateWebhookEndpointInput, PrismWebhookEndpoint, PrismWebhookEndpointCreated, PrismWebhookParseInput, PrismWebhookParseResult } from './types';
|
|
3
3
|
interface MerchantAuthenticationPort {
|
|
4
4
|
createClientAuthenticationToken(input: types.IMerchantAuthenticationServiceCreateClientAuthenticationTokenRequest): Promise<types.MerchantAuthenticationServiceCreateClientAuthenticationTokenResponse>;
|
|
5
5
|
}
|
|
6
6
|
interface PaymentPort {
|
|
7
|
-
incrementalAuthorization?(input: types.IPaymentServiceIncrementalAuthorizationRequest): Promise<types.PaymentServiceIncrementalAuthorizationResponse>;
|
|
8
7
|
get(input: types.IPaymentServiceGetRequest): Promise<types.PaymentServiceGetResponse>;
|
|
9
8
|
capture(input: types.IPaymentServiceCaptureRequest): Promise<types.PaymentServiceCaptureResponse>;
|
|
10
9
|
void(input: types.IPaymentServiceVoidRequest): Promise<types.PaymentServiceVoidResponse>;
|
|
@@ -37,9 +36,6 @@ export declare class PrismStripeConnector implements PrismStripeConnectorV1 {
|
|
|
37
36
|
retrievePayment(input: PrismRetrievePaymentInput): Promise<PrismPaymentResult>;
|
|
38
37
|
establishReusableReference(input: PrismEstablishReusableReferenceInput): Promise<PrismReusableReferenceDerivation>;
|
|
39
38
|
findPaymentsByMerchantOperation(input: PrismFindPaymentInput): Promise<PrismPaymentResult[]>;
|
|
40
|
-
retrieveAuthorization(input: PrismStripeAuthorizationOwner): Promise<PrismStripeAuthorization>;
|
|
41
|
-
incrementAuthorization(input: PrismStripeIncrementAuthorizationInput): Promise<PrismStripeAuthorization>;
|
|
42
|
-
captureAuthorization(input: PrismStripeCaptureAuthorizationInput): Promise<PrismStripeAuthorization>;
|
|
43
39
|
capturePayment(input: PrismCaptureInput): Promise<PrismPaymentResult>;
|
|
44
40
|
cancelPayment(input: PrismCancelInput): Promise<PrismPaymentResult>;
|
|
45
41
|
refundPayment(input: PrismRefundInput): Promise<PrismRefundResult>;
|
|
@@ -735,7 +735,6 @@ class PrismStripeConnector {
|
|
|
735
735
|
: 'validation',
|
|
736
736
|
...(providerCode ? { providerCode } : {}),
|
|
737
737
|
...(requestId ? { providerRequestId: requestId } : {}),
|
|
738
|
-
providerHttpStatus: response.status,
|
|
739
738
|
retryable: response.status === 429 || response.status >= 500,
|
|
740
739
|
outcome: response.status >= 500 ? 'ambiguous' : 'definitely_not_applied',
|
|
741
740
|
safeMessage: 'Stripe rejected the payment operation.',
|
|
@@ -765,13 +764,11 @@ class PrismStripeConnector {
|
|
|
765
764
|
payment: {
|
|
766
765
|
amount: moneyMessage(input.money),
|
|
767
766
|
connectorCustomerId: input.customerReference,
|
|
768
|
-
setupFutureUsage: input.
|
|
769
|
-
Boolean(input.futureUse) ||
|
|
767
|
+
setupFutureUsage: Boolean(input.futureUse) ||
|
|
770
768
|
input.metadata.alphaweb_setup_future_usage === 'off_session'
|
|
771
769
|
? proto_1.types.FutureUsage.OFF_SESSION
|
|
772
770
|
: proto_1.types.FutureUsage.FUTURE_USAGE_UNSPECIFIED,
|
|
773
|
-
captureMethod: input.
|
|
774
|
-
input.metadata.alphaweb_capture_method === 'manual'
|
|
771
|
+
captureMethod: input.metadata.alphaweb_capture_method === 'manual'
|
|
775
772
|
? proto_1.types.CaptureMethod.MANUAL
|
|
776
773
|
: proto_1.types.CaptureMethod.AUTOMATIC,
|
|
777
774
|
paymentMethodConfigurationId: input.methodSelection.paymentMethodConfigurationId,
|
|
@@ -1076,124 +1073,6 @@ class PrismStripeConnector {
|
|
|
1076
1073
|
}
|
|
1077
1074
|
throw validationError('Stripe payment search exceeded the bounded reconciliation window.', 'PRISM_SEARCH_LIMIT');
|
|
1078
1075
|
}
|
|
1079
|
-
async retrieveAuthorization(input) {
|
|
1080
|
-
assertIdentifier(input.rootOperationId, 'rootOperationId');
|
|
1081
|
-
assertIdentifier(input.connectorPaymentId, 'connectorPaymentId');
|
|
1082
|
-
if (input.credentialBindingId !== this.options.credentialBindingId ||
|
|
1083
|
-
input.providerAccountId !== this.options.providerAccountId ||
|
|
1084
|
-
input.livemode !== (this.options.environment === 'live')) {
|
|
1085
|
-
throw configurationError('Stripe authorization ownership does not match runtime.', 'PRISM_PAYMENT_OWNERSHIP');
|
|
1086
|
-
}
|
|
1087
|
-
const raw = recordOf(await this.stripeRequest('GET', `/payment_intents/${encodeURIComponent(input.connectorPaymentId)}?expand[]=latest_charge`));
|
|
1088
|
-
const intent = parseStripePaymentIntent(raw);
|
|
1089
|
-
const capturable = integerOf(raw.amount_capturable);
|
|
1090
|
-
const received = integerOf(raw.amount_received);
|
|
1091
|
-
const charge = recordOf(raw.latest_charge);
|
|
1092
|
-
if (intent.id !== input.connectorPaymentId || intent.metadata.alphaweb_operation_id !== input.rootOperationId ||
|
|
1093
|
-
intent.metadata.alphaweb_operation_kind !== 'combined_checkout' || intent.livemode !== input.livemode ||
|
|
1094
|
-
raw.capture_method !== 'manual' || capturable === null || received === null || capturable < 0 || received < 0 ||
|
|
1095
|
-
!Number.isSafeInteger(capturable) || !Number.isSafeInteger(received)) {
|
|
1096
|
-
throw configurationError('Stripe authorization does not match its checkout.', 'PRISM_PAYMENT_OWNERSHIP');
|
|
1097
|
-
}
|
|
1098
|
-
(0, money_1.assertSafeProviderMoney)({ amountMinor: intent.amount.toString(), currency: intent.currency });
|
|
1099
|
-
const captureId = stringOf(charge.id);
|
|
1100
|
-
if (received > 0 && (!captureId || charge.payment_intent !== intent.id ||
|
|
1101
|
-
charge.livemode !== input.livemode || charge.captured !== true || charge.paid !== true ||
|
|
1102
|
-
charge.amount_captured !== received || charge.currency !== intent.currency)) {
|
|
1103
|
-
throw configurationError('Stripe capture evidence does not match its checkout.', 'PRISM_CAPTURE_OWNERSHIP');
|
|
1104
|
-
}
|
|
1105
|
-
const card = recordOf(recordOf(charge.payment_method_details).card);
|
|
1106
|
-
const clientSecret = stringOf(raw.client_secret);
|
|
1107
|
-
return {
|
|
1108
|
-
connectorPaymentId: intent.id, status: normalizedStripeIntentStatus(intent),
|
|
1109
|
-
money: { amountMinor: intent.amount.toString(), currency: intent.currency },
|
|
1110
|
-
amountCapturable: capturable.toString(), amountReceived: received.toString(),
|
|
1111
|
-
incrementalAuthorizationAvailable: recordOf(card.incremental_authorization).status === 'available',
|
|
1112
|
-
lastAdjustmentId: intent.metadata.alphaweb_adjustment_id ?? null,
|
|
1113
|
-
connectorCaptureId: received > 0 ? captureId : null,
|
|
1114
|
-
...(['requires_payment_method', 'requires_customer_action'].includes(normalizedStripeIntentStatus(intent)) &&
|
|
1115
|
-
clientSecret?.startsWith(`${intent.id}_secret_`) ? { clientSecret } : {}),
|
|
1116
|
-
};
|
|
1117
|
-
}
|
|
1118
|
-
async incrementAuthorization(input) {
|
|
1119
|
-
assertIdentifier(input.idempotencyKey, 'idempotencyKey');
|
|
1120
|
-
const target = (0, money_1.assertSafeProviderMoney)(input.money);
|
|
1121
|
-
const expected = (0, money_1.assertSafeProviderMoney)(input.expectedMoney);
|
|
1122
|
-
const observed = await this.retrieveAuthorization(input);
|
|
1123
|
-
if (observed.status !== 'authorized' || observed.amountReceived !== '0' ||
|
|
1124
|
-
observed.money.currency !== target.currency || expected.currency !== target.currency ||
|
|
1125
|
-
BigInt(observed.amountCapturable) < BigInt(observed.money.amountMinor)) {
|
|
1126
|
-
throw validationError('Stripe authorization cannot be adjusted in this state.', 'PRISM_AUTHORIZATION_STATE');
|
|
1127
|
-
}
|
|
1128
|
-
if (observed.lastAdjustmentId === input.idempotencyKey && observed.money.amountMinor === input.money.amountMinor)
|
|
1129
|
-
return observed;
|
|
1130
|
-
if (BigInt(observed.amountCapturable) < BigInt(input.expectedMoney.amountMinor)) {
|
|
1131
|
-
throw configurationError('Stripe no longer covers the approved selection.', 'PRISM_AUTHORIZATION_AMOUNT');
|
|
1132
|
-
}
|
|
1133
|
-
// A smaller final total is captured for less, without modifying the hold.
|
|
1134
|
-
if (BigInt(input.money.amountMinor) <= BigInt(observed.amountCapturable))
|
|
1135
|
-
return observed;
|
|
1136
|
-
if (!observed.incrementalAuthorizationAvailable) {
|
|
1137
|
-
throw validationError('This payment cannot authorize a higher total. The offer was not added.', 'PRISM_INCREMENT_UNAVAILABLE');
|
|
1138
|
-
}
|
|
1139
|
-
const increment = this.dependencies.payments.incrementalAuthorization;
|
|
1140
|
-
if (!increment)
|
|
1141
|
-
throw configurationError('Prism lacks incremental authorization.', 'PRISM_CAPABILITY_UNSUPPORTED');
|
|
1142
|
-
try {
|
|
1143
|
-
const result = await increment.call(this.dependencies.payments, {
|
|
1144
|
-
merchantAuthorizationId: input.idempotencyKey,
|
|
1145
|
-
connectorTransactionId: input.connectorPaymentId, amount: moneyMessage(input.money),
|
|
1146
|
-
});
|
|
1147
|
-
const error = recordOf(result.error);
|
|
1148
|
-
if (result.statusCode >= 400 || Object.keys(error).length) {
|
|
1149
|
-
throw new error_1.PrismOperationError({
|
|
1150
|
-
category: result.statusCode >= 500 ? 'provider_server' : 'declined',
|
|
1151
|
-
outcome: result.statusCode >= 500 ? 'ambiguous' : 'definitely_not_applied', retryable: false,
|
|
1152
|
-
providerHttpStatus: result.statusCode, providerCode: stringOf(error.code) ?? undefined,
|
|
1153
|
-
safeMessage: 'Stripe did not authorize the higher total. The offer was not added.',
|
|
1154
|
-
});
|
|
1155
|
-
}
|
|
1156
|
-
}
|
|
1157
|
-
catch (error) {
|
|
1158
|
-
throw (0, error_1.normalizePrismError)(error);
|
|
1159
|
-
}
|
|
1160
|
-
let aligned;
|
|
1161
|
-
try {
|
|
1162
|
-
aligned = await this.retrieveAuthorization(input);
|
|
1163
|
-
}
|
|
1164
|
-
catch (error) {
|
|
1165
|
-
throw new error_1.PrismOperationError({ ...(0, error_1.normalizePrismError)(error).shape, outcome: 'ambiguous', retryable: false });
|
|
1166
|
-
}
|
|
1167
|
-
if (aligned.lastAdjustmentId !== input.idempotencyKey || aligned.money.amountMinor !== input.money.amountMinor ||
|
|
1168
|
-
aligned.status !== 'authorized' || BigInt(aligned.amountCapturable) < BigInt(input.money.amountMinor)) {
|
|
1169
|
-
throw new error_1.PrismOperationError({ category: 'unknown', outcome: 'ambiguous', retryable: false,
|
|
1170
|
-
providerCode: 'PRISM_INCREMENT_EVIDENCE', safeMessage: 'Stripe authorization update is awaiting verification.' });
|
|
1171
|
-
}
|
|
1172
|
-
return aligned;
|
|
1173
|
-
}
|
|
1174
|
-
async captureAuthorization(input) {
|
|
1175
|
-
assertIdentifier(input.idempotencyKey, 'idempotencyKey');
|
|
1176
|
-
const target = (0, money_1.assertSafeProviderMoney)(input.money);
|
|
1177
|
-
const observed = await this.retrieveAuthorization(input);
|
|
1178
|
-
if (observed.money.currency !== target.currency)
|
|
1179
|
-
throw configurationError('Stripe capture currency changed.', 'PRISM_CAPTURE_OWNERSHIP');
|
|
1180
|
-
if (observed.status === 'captured' && observed.amountReceived === input.money.amountMinor)
|
|
1181
|
-
return observed;
|
|
1182
|
-
if (observed.status !== 'authorized' || observed.amountReceived !== '0' || BigInt(observed.amountCapturable) < BigInt(input.money.amountMinor)) {
|
|
1183
|
-
throw validationError('Stripe has not authorized the final checkout total.', 'PRISM_CAPTURE_AMOUNT');
|
|
1184
|
-
}
|
|
1185
|
-
const result = await this.capturePayment({ operationId: input.rootOperationId, idempotencyKey: input.idempotencyKey,
|
|
1186
|
-
connectorPaymentId: input.connectorPaymentId, money: input.money });
|
|
1187
|
-
if (result.status === 'failed')
|
|
1188
|
-
throw new error_1.PrismOperationError({ category: 'declined',
|
|
1189
|
-
outcome: 'definitely_not_applied', retryable: false, safeMessage: 'Stripe declined the final capture.' });
|
|
1190
|
-
try {
|
|
1191
|
-
return await this.retrieveAuthorization(input);
|
|
1192
|
-
}
|
|
1193
|
-
catch (error) {
|
|
1194
|
-
throw new error_1.PrismOperationError({ ...(0, error_1.normalizePrismError)(error).shape, outcome: 'ambiguous', retryable: false });
|
|
1195
|
-
}
|
|
1196
|
-
}
|
|
1197
1076
|
async capturePayment(input) {
|
|
1198
1077
|
assertIdentifier(input.idempotencyKey, 'idempotencyKey');
|
|
1199
1078
|
try {
|
|
@@ -5,7 +5,7 @@ export type PrismEnvironment = 'test' | 'live';
|
|
|
5
5
|
* payment contract a Stripe/PayPal-only union.
|
|
6
6
|
*/
|
|
7
7
|
export type PrismConnectorId = string;
|
|
8
|
-
export type PrismOperationKind = 'checkout' | '
|
|
8
|
+
export type PrismOperationKind = 'checkout' | 'post_purchase' | 'subscription_renewal' | 'refund' | 'cancel';
|
|
9
9
|
export interface PrismMoney {
|
|
10
10
|
amountMinor: string;
|
|
11
11
|
currency: string;
|
|
@@ -25,6 +25,11 @@ export interface PrismRecurringAgreement {
|
|
|
25
25
|
export interface PrismFutureUseIntent {
|
|
26
26
|
purposes: [PrismFutureUsePurpose, ...PrismFutureUsePurpose[]];
|
|
27
27
|
recurringAgreement?: PrismRecurringAgreement;
|
|
28
|
+
/** Independent merchant-managed Stripe plans. Mutually exclusive with the legacy single agreement. */
|
|
29
|
+
recurringAgreements?: Array<PrismRecurringAgreement & {
|
|
30
|
+
planKey: string;
|
|
31
|
+
quantity: number;
|
|
32
|
+
}>;
|
|
28
33
|
}
|
|
29
34
|
export interface PrismBrowserContinuation {
|
|
30
35
|
returnUrl: string;
|
|
@@ -58,8 +63,6 @@ export interface PrismPayPalBrowserSession {
|
|
|
58
63
|
clientId: string;
|
|
59
64
|
environment: 'sandbox' | 'production';
|
|
60
65
|
savePayment: boolean;
|
|
61
|
-
/** False for an approved order whose final total is selected before capture. */
|
|
62
|
-
commit?: boolean;
|
|
63
66
|
}
|
|
64
67
|
/**
|
|
65
68
|
* Declaration-merging extension point for future qualified connectors.
|
|
@@ -119,7 +122,6 @@ export interface PrismPaymentResult {
|
|
|
119
122
|
connectorMetadata?: string;
|
|
120
123
|
reusableReference?: PrismReusablePaymentReference;
|
|
121
124
|
providerRequestId?: string;
|
|
122
|
-
providerHttpStatus?: number;
|
|
123
125
|
failure?: PrismPaymentFailure;
|
|
124
126
|
}
|
|
125
127
|
export interface PrismRefundResult {
|
|
@@ -327,39 +329,7 @@ export interface PrismConnectorV1 {
|
|
|
327
329
|
deleteWebhookEndpoint(input: PrismDeleteWebhookEndpointInput): Promise<void>;
|
|
328
330
|
retrieveWebhookEndpoint(input: PrismRetrieveWebhookEndpointInput): Promise<PrismWebhookEndpoint>;
|
|
329
331
|
}
|
|
330
|
-
export
|
|
331
|
-
rootOperationId: string;
|
|
332
|
-
connectorPaymentId: string;
|
|
333
|
-
credentialBindingId: string;
|
|
334
|
-
providerAccountId: string;
|
|
335
|
-
livemode: boolean;
|
|
336
|
-
}
|
|
337
|
-
export interface PrismStripeAuthorization {
|
|
338
|
-
connectorPaymentId: string;
|
|
339
|
-
status: PrismNormalizedPaymentStatus;
|
|
340
|
-
money: PrismMoney;
|
|
341
|
-
amountCapturable: string;
|
|
342
|
-
amountReceived: string;
|
|
343
|
-
incrementalAuthorizationAvailable: boolean;
|
|
344
|
-
lastAdjustmentId: string | null;
|
|
345
|
-
connectorCaptureId: string | null;
|
|
346
|
-
/** Only returned for an unconfirmed, owned PaymentIntent. Never log this. */
|
|
347
|
-
clientSecret?: string;
|
|
348
|
-
}
|
|
349
|
-
export interface PrismStripeIncrementAuthorizationInput extends PrismStripeAuthorizationOwner {
|
|
350
|
-
expectedMoney: PrismMoney;
|
|
351
|
-
money: PrismMoney;
|
|
352
|
-
idempotencyKey: string;
|
|
353
|
-
}
|
|
354
|
-
export interface PrismStripeCaptureAuthorizationInput extends PrismStripeAuthorizationOwner {
|
|
355
|
-
money: PrismMoney;
|
|
356
|
-
idempotencyKey: string;
|
|
357
|
-
}
|
|
358
|
-
export interface PrismStripeConnectorV1 extends PrismConnectorV1 {
|
|
359
|
-
retrieveAuthorization?(input: PrismStripeAuthorizationOwner): Promise<PrismStripeAuthorization>;
|
|
360
|
-
incrementAuthorization?(input: PrismStripeIncrementAuthorizationInput): Promise<PrismStripeAuthorization>;
|
|
361
|
-
captureAuthorization?(input: PrismStripeCaptureAuthorizationInput): Promise<PrismStripeAuthorization>;
|
|
362
|
-
}
|
|
332
|
+
export type PrismStripeConnectorV1 = PrismConnectorV1;
|
|
363
333
|
export type PrismPayPalOrderLifecycleStatus = 'CREATED' | 'SAVED' | 'PAYER_ACTION_REQUIRED' | 'APPROVED' | 'VOIDED' | 'COMPLETED' | 'UNKNOWN';
|
|
364
334
|
export interface PrismPayPalOrderLifecycle {
|
|
365
335
|
orderStatus: PrismPayPalOrderLifecycleStatus;
|
|
@@ -369,69 +339,9 @@ export interface PrismPayPalOrderLifecycle {
|
|
|
369
339
|
export interface PrismRetrieveOrderLifecycleInput {
|
|
370
340
|
connectorPaymentId: string;
|
|
371
341
|
}
|
|
372
|
-
export interface PrismPayPalOrderItem {
|
|
373
|
-
name: string;
|
|
374
|
-
sku?: string;
|
|
375
|
-
category?: 'DIGITAL_GOODS' | 'PHYSICAL_GOODS' | 'DONATION';
|
|
376
|
-
quantity: number;
|
|
377
|
-
unitAmountMinor: string;
|
|
378
|
-
taxMinor?: string;
|
|
379
|
-
/** Existing merchant-managed recurring disclosure; never inferred from an OTO total. */
|
|
380
|
-
recurringAgreement?: PrismRecurringAgreement;
|
|
381
|
-
}
|
|
382
|
-
export interface PrismPayPalOrderQuote {
|
|
383
|
-
money: PrismMoney;
|
|
384
|
-
breakdown?: {
|
|
385
|
-
itemTotalMinor?: string;
|
|
386
|
-
taxTotalMinor?: string;
|
|
387
|
-
shippingMinor?: string;
|
|
388
|
-
handlingMinor?: string;
|
|
389
|
-
insuranceMinor?: string;
|
|
390
|
-
shippingDiscountMinor?: string;
|
|
391
|
-
discountMinor?: string;
|
|
392
|
-
};
|
|
393
|
-
items?: PrismPayPalOrderItem[];
|
|
394
|
-
}
|
|
395
|
-
export interface PrismPayPalOrderOwner {
|
|
396
|
-
connectorPaymentId: string;
|
|
397
|
-
/** The original order-creation operation, never the later adjustment/capture. */
|
|
398
|
-
rootOperationId: string;
|
|
399
|
-
credentialBindingId: string;
|
|
400
|
-
providerAccountId: string;
|
|
401
|
-
livemode: boolean;
|
|
402
|
-
expectedCustomId?: string;
|
|
403
|
-
}
|
|
404
|
-
export interface PrismPayPalApprovedOrder extends PrismPayPalOrderLifecycle {
|
|
405
|
-
connectorPaymentId: string;
|
|
406
|
-
rootOperationId: string;
|
|
407
|
-
quote: PrismPayPalOrderQuote;
|
|
408
|
-
providerUpdatedAt?: string;
|
|
409
|
-
providerRequestId?: string;
|
|
410
|
-
/** Native Prism metadata, retained for refunds even after a lost capture response. */
|
|
411
|
-
connectorMetadata?: string;
|
|
412
|
-
capture?: {
|
|
413
|
-
connectorCaptureId: string;
|
|
414
|
-
status: 'pending' | 'captured' | 'failed' | 'refunded' | 'partially_refunded' | 'unknown';
|
|
415
|
-
money: PrismMoney;
|
|
416
|
-
};
|
|
417
|
-
}
|
|
418
|
-
export interface PrismPayPalUpdateApprovedOrderInput extends PrismPayPalOrderOwner {
|
|
419
|
-
expectedQuote: PrismPayPalOrderQuote;
|
|
420
|
-
quote: PrismPayPalOrderQuote;
|
|
421
|
-
}
|
|
422
|
-
export interface PrismPayPalCaptureApprovedOrderInput extends PrismPayPalOrderOwner {
|
|
423
|
-
quote: PrismPayPalOrderQuote;
|
|
424
|
-
/** Persist once for the final capture; must not change when a claim is renewed. */
|
|
425
|
-
idempotencyKey: string;
|
|
426
|
-
}
|
|
427
342
|
export interface PrismPayPalConnectorV1 extends PrismConnectorV1 {
|
|
428
343
|
authorizeApprovedPayment(input: PrismAuthorizeApprovedPaymentInput): Promise<PrismPaymentResult>;
|
|
429
344
|
retrieveOrderLifecycle?(input: PrismRetrieveOrderLifecycleInput): Promise<PrismPayPalOrderLifecycle>;
|
|
430
|
-
createAdjustableOrder?(input: PrismMerchantOperation): Promise<PrismPaymentResult>;
|
|
431
|
-
retrieveApprovedOrder?(input: PrismPayPalOrderOwner): Promise<PrismPayPalApprovedOrder>;
|
|
432
|
-
/** Caller serializes decisions and reconciles an uncertain PATCH before another mutation. */
|
|
433
|
-
updateApprovedOrder?(input: PrismPayPalUpdateApprovedOrderInput): Promise<PrismPayPalApprovedOrder>;
|
|
434
|
-
captureApprovedOrder?(input: PrismPayPalCaptureApprovedOrderInput): Promise<PrismPayPalApprovedOrder>;
|
|
435
345
|
}
|
|
436
346
|
export interface PrismStripeConnectorOptions {
|
|
437
347
|
environment: PrismEnvironment;
|
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
import { PrismOperationError } from './error';
|
|
2
|
-
import type { PrismPayPalApprovedOrder, PrismPayPalConnectorOptions, PrismPayPalOrderOwner, PrismPayPalOrderQuote } from './types';
|
|
3
|
-
export declare function approvedOrderError(code: string, outcome?: 'ambiguous' | 'definitely_not_applied'): PrismOperationError;
|
|
4
|
-
export declare function paypalQuotePayload(quote: PrismPayPalOrderQuote): {
|
|
5
|
-
items?: {
|
|
6
|
-
billing_plan?: {
|
|
7
|
-
name: string;
|
|
8
|
-
billing_cycles: {
|
|
9
|
-
tenure_type: string;
|
|
10
|
-
pricing_scheme: {
|
|
11
|
-
price: {
|
|
12
|
-
currency_code: string;
|
|
13
|
-
value: string;
|
|
14
|
-
};
|
|
15
|
-
pricing_model: string;
|
|
16
|
-
};
|
|
17
|
-
frequency: {
|
|
18
|
-
interval_unit: string;
|
|
19
|
-
interval_count: number;
|
|
20
|
-
};
|
|
21
|
-
total_cycles: number;
|
|
22
|
-
sequence: number;
|
|
23
|
-
}[];
|
|
24
|
-
} | undefined;
|
|
25
|
-
tax?: {
|
|
26
|
-
currency_code: string;
|
|
27
|
-
value: string;
|
|
28
|
-
} | undefined;
|
|
29
|
-
category?: "DIGITAL_GOODS" | "PHYSICAL_GOODS" | "DONATION" | undefined;
|
|
30
|
-
sku?: string | undefined;
|
|
31
|
-
name: string;
|
|
32
|
-
quantity: string;
|
|
33
|
-
unit_amount: {
|
|
34
|
-
currency_code: string;
|
|
35
|
-
value: string;
|
|
36
|
-
};
|
|
37
|
-
}[] | undefined;
|
|
38
|
-
amount: {
|
|
39
|
-
breakdown?: Record<string, {
|
|
40
|
-
currency_code: string;
|
|
41
|
-
value: string;
|
|
42
|
-
}> | undefined;
|
|
43
|
-
currency_code: string;
|
|
44
|
-
value: string;
|
|
45
|
-
};
|
|
46
|
-
};
|
|
47
|
-
export declare function paypalQuotesMatch(left: PrismPayPalOrderQuote, right: PrismPayPalOrderQuote): boolean;
|
|
48
|
-
export declare function assertRecurringDisclosurePreserved(previous: PrismPayPalOrderQuote, next: PrismPayPalOrderQuote): void;
|
|
49
|
-
export declare function readApprovedOrder(payload: unknown, owner: PrismPayPalOrderOwner, options: Readonly<PrismPayPalConnectorOptions>): PrismPayPalApprovedOrder;
|
|
@@ -1,326 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.approvedOrderError = approvedOrderError;
|
|
4
|
-
exports.paypalQuotePayload = paypalQuotePayload;
|
|
5
|
-
exports.paypalQuotesMatch = paypalQuotesMatch;
|
|
6
|
-
exports.assertRecurringDisclosurePreserved = assertRecurringDisclosurePreserved;
|
|
7
|
-
exports.readApprovedOrder = readApprovedOrder;
|
|
8
|
-
const node_util_1 = require("node:util");
|
|
9
|
-
const error_1 = require("./error");
|
|
10
|
-
const money_1 = require("./money");
|
|
11
|
-
const breakdownFields = {
|
|
12
|
-
itemTotalMinor: 'item_total',
|
|
13
|
-
taxTotalMinor: 'tax_total',
|
|
14
|
-
shippingMinor: 'shipping',
|
|
15
|
-
handlingMinor: 'handling',
|
|
16
|
-
insuranceMinor: 'insurance',
|
|
17
|
-
shippingDiscountMinor: 'shipping_discount',
|
|
18
|
-
discountMinor: 'discount',
|
|
19
|
-
};
|
|
20
|
-
function record(value) {
|
|
21
|
-
return typeof value === 'object' && value !== null && !Array.isArray(value)
|
|
22
|
-
? value
|
|
23
|
-
: {};
|
|
24
|
-
}
|
|
25
|
-
function text(value) {
|
|
26
|
-
return typeof value === 'string' ? value : '';
|
|
27
|
-
}
|
|
28
|
-
function approvedOrderError(code, outcome = 'ambiguous') {
|
|
29
|
-
return new error_1.PrismOperationError({
|
|
30
|
-
category: 'conflict',
|
|
31
|
-
providerCode: code,
|
|
32
|
-
retryable: false,
|
|
33
|
-
outcome,
|
|
34
|
-
safeMessage: 'The PayPal order must be verified before payment can continue.',
|
|
35
|
-
});
|
|
36
|
-
}
|
|
37
|
-
function moneyFrom(value, currency) {
|
|
38
|
-
const amount = record(value);
|
|
39
|
-
const code = text(amount.currency_code).toLowerCase();
|
|
40
|
-
if (!code || (currency && currency !== code) || typeof amount.value !== 'string') {
|
|
41
|
-
throw approvedOrderError('PRISM_PAYPAL_ORDER_MONEY');
|
|
42
|
-
}
|
|
43
|
-
return {
|
|
44
|
-
currency: code,
|
|
45
|
-
amountMinor: (0, money_1.toProviderMinorAmount)({ medusaAmount: amount.value, currency: code }).toString(),
|
|
46
|
-
};
|
|
47
|
-
}
|
|
48
|
-
function providerMoney(amountMinor, currency) {
|
|
49
|
-
return {
|
|
50
|
-
currency_code: currency.toUpperCase(),
|
|
51
|
-
value: (0, money_1.fromProviderMinorAmount)({ amountMinor, currency }),
|
|
52
|
-
};
|
|
53
|
-
}
|
|
54
|
-
function minor(value, currency) {
|
|
55
|
-
(0, money_1.fromProviderMinorAmount)({ amountMinor: value, currency });
|
|
56
|
-
return BigInt(value);
|
|
57
|
-
}
|
|
58
|
-
function recurringPlan(agreement, currency) {
|
|
59
|
-
const limits = { day: 365, week: 52, month: 12, year: 1 };
|
|
60
|
-
const money = (0, money_1.assertSafeProviderMoney)(agreement.recurringMoney);
|
|
61
|
-
if (money.currency !== currency ||
|
|
62
|
-
!agreement.planName.trim() ||
|
|
63
|
-
agreement.planName.length > 127 ||
|
|
64
|
-
!Number.isSafeInteger(agreement.intervalCount) ||
|
|
65
|
-
agreement.intervalCount < 1 ||
|
|
66
|
-
!(agreement.intervalUnit in limits) ||
|
|
67
|
-
agreement.intervalCount > limits[agreement.intervalUnit] ||
|
|
68
|
-
(agreement.totalPayments !== null &&
|
|
69
|
-
(!Number.isSafeInteger(agreement.totalPayments) ||
|
|
70
|
-
agreement.totalPayments < 2 ||
|
|
71
|
-
agreement.totalPayments > 999))) {
|
|
72
|
-
throw approvedOrderError('PRISM_PAYPAL_RECURRING_AGREEMENT', 'definitely_not_applied');
|
|
73
|
-
}
|
|
74
|
-
return {
|
|
75
|
-
name: agreement.planName,
|
|
76
|
-
billing_cycles: [
|
|
77
|
-
{
|
|
78
|
-
tenure_type: 'REGULAR',
|
|
79
|
-
pricing_scheme: {
|
|
80
|
-
price: providerMoney(agreement.recurringMoney.amountMinor, currency),
|
|
81
|
-
pricing_model: 'FIXED',
|
|
82
|
-
},
|
|
83
|
-
frequency: {
|
|
84
|
-
interval_unit: agreement.intervalUnit.toUpperCase(),
|
|
85
|
-
interval_count: agreement.intervalCount,
|
|
86
|
-
},
|
|
87
|
-
total_cycles: agreement.totalPayments ?? 0,
|
|
88
|
-
sequence: 1,
|
|
89
|
-
},
|
|
90
|
-
],
|
|
91
|
-
};
|
|
92
|
-
}
|
|
93
|
-
function recurringFrom(value, currency) {
|
|
94
|
-
const plan = record(value);
|
|
95
|
-
const cycles = Array.isArray(plan.billing_cycles) ? plan.billing_cycles : [];
|
|
96
|
-
const cycle = record(cycles[0]);
|
|
97
|
-
const frequency = record(cycle.frequency);
|
|
98
|
-
const pricing = record(cycle.pricing_scheme);
|
|
99
|
-
const unit = text(frequency.interval_unit).toLowerCase();
|
|
100
|
-
if (cycles.length !== 1 ||
|
|
101
|
-
cycle.tenure_type !== 'REGULAR' ||
|
|
102
|
-
cycle.sequence !== 1 ||
|
|
103
|
-
pricing.pricing_model !== 'FIXED' ||
|
|
104
|
-
(unit !== 'day' && unit !== 'week' && unit !== 'month' && unit !== 'year')) {
|
|
105
|
-
throw approvedOrderError('PRISM_PAYPAL_RECURRING_AGREEMENT');
|
|
106
|
-
}
|
|
107
|
-
const agreement = {
|
|
108
|
-
planName: text(plan.name),
|
|
109
|
-
intervalUnit: unit,
|
|
110
|
-
intervalCount: Number(frequency.interval_count),
|
|
111
|
-
totalPayments: cycle.total_cycles === 0 ? null : Number(cycle.total_cycles),
|
|
112
|
-
recurringMoney: moneyFrom(pricing.price, currency),
|
|
113
|
-
};
|
|
114
|
-
recurringPlan(agreement, currency);
|
|
115
|
-
return agreement;
|
|
116
|
-
}
|
|
117
|
-
function buildQuotePayload(quote) {
|
|
118
|
-
const currency = (0, money_1.assertSafeProviderMoney)(quote.money).currency;
|
|
119
|
-
const breakdown = {};
|
|
120
|
-
let total = 0n;
|
|
121
|
-
for (const [key, field] of Object.entries(breakdownFields)) {
|
|
122
|
-
const value = quote.breakdown?.[key];
|
|
123
|
-
if (value === undefined)
|
|
124
|
-
continue;
|
|
125
|
-
const amount = minor(value, currency);
|
|
126
|
-
total += field === 'discount' || field === 'shipping_discount' ? -amount : amount;
|
|
127
|
-
breakdown[field] = providerMoney(value, currency);
|
|
128
|
-
}
|
|
129
|
-
if (quote.breakdown && total !== BigInt(quote.money.amountMinor)) {
|
|
130
|
-
throw approvedOrderError('PRISM_PAYPAL_QUOTE_TOTAL', 'definitely_not_applied');
|
|
131
|
-
}
|
|
132
|
-
let itemTotal = 0n;
|
|
133
|
-
let taxTotal = 0n;
|
|
134
|
-
const items = quote.items?.map((item) => {
|
|
135
|
-
if (!item.name.trim() ||
|
|
136
|
-
item.name.length > 127 ||
|
|
137
|
-
(item.sku !== undefined && (!item.sku || item.sku.length > 127)) ||
|
|
138
|
-
(item.category !== undefined &&
|
|
139
|
-
!['DIGITAL_GOODS', 'PHYSICAL_GOODS', 'DONATION'].includes(item.category)) ||
|
|
140
|
-
!Number.isSafeInteger(item.quantity) ||
|
|
141
|
-
item.quantity < 1) {
|
|
142
|
-
throw approvedOrderError('PRISM_PAYPAL_QUOTE_ITEM', 'definitely_not_applied');
|
|
143
|
-
}
|
|
144
|
-
itemTotal += minor(item.unitAmountMinor, currency) * BigInt(item.quantity);
|
|
145
|
-
taxTotal += minor(item.taxMinor ?? '0', currency) * BigInt(item.quantity);
|
|
146
|
-
return {
|
|
147
|
-
name: item.name,
|
|
148
|
-
quantity: String(item.quantity),
|
|
149
|
-
unit_amount: providerMoney(item.unitAmountMinor, currency),
|
|
150
|
-
...(item.sku ? { sku: item.sku } : {}),
|
|
151
|
-
...(item.category ? { category: item.category } : {}),
|
|
152
|
-
...(item.taxMinor === undefined ? {} : { tax: providerMoney(item.taxMinor, currency) }),
|
|
153
|
-
...(item.recurringAgreement
|
|
154
|
-
? { billing_plan: recurringPlan(item.recurringAgreement, currency) }
|
|
155
|
-
: {}),
|
|
156
|
-
};
|
|
157
|
-
});
|
|
158
|
-
if (items &&
|
|
159
|
-
(!items.length ||
|
|
160
|
-
quote.breakdown?.itemTotalMinor === undefined ||
|
|
161
|
-
itemTotal !== BigInt(quote.breakdown.itemTotalMinor) ||
|
|
162
|
-
(quote.items?.some((item) => item.taxMinor !== undefined) &&
|
|
163
|
-
(quote.breakdown.taxTotalMinor === undefined ||
|
|
164
|
-
taxTotal !== BigInt(quote.breakdown.taxTotalMinor))))) {
|
|
165
|
-
throw approvedOrderError('PRISM_PAYPAL_QUOTE_ITEMS_TOTAL', 'definitely_not_applied');
|
|
166
|
-
}
|
|
167
|
-
return {
|
|
168
|
-
amount: {
|
|
169
|
-
...providerMoney(quote.money.amountMinor, currency),
|
|
170
|
-
...(quote.breakdown ? { breakdown } : {}),
|
|
171
|
-
},
|
|
172
|
-
...(items ? { items } : {}),
|
|
173
|
-
};
|
|
174
|
-
}
|
|
175
|
-
function paypalQuotePayload(quote) {
|
|
176
|
-
try {
|
|
177
|
-
return buildQuotePayload(quote);
|
|
178
|
-
}
|
|
179
|
-
catch (error) {
|
|
180
|
-
if (error instanceof error_1.PrismOperationError)
|
|
181
|
-
throw error;
|
|
182
|
-
throw approvedOrderError('PRISM_PAYPAL_QUOTE_INVALID', 'definitely_not_applied');
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
function paypalQuotesMatch(left, right) {
|
|
186
|
-
return (0, node_util_1.isDeepStrictEqual)(paypalQuotePayload(left), paypalQuotePayload(right));
|
|
187
|
-
}
|
|
188
|
-
function assertRecurringDisclosurePreserved(previous, next) {
|
|
189
|
-
const agreements = (quote) => quote.items
|
|
190
|
-
?.filter((item) => item.recurringAgreement)
|
|
191
|
-
.map((item) => ({ quantity: item.quantity, agreement: item.recurringAgreement })) ?? [];
|
|
192
|
-
if (!(0, node_util_1.isDeepStrictEqual)(agreements(previous), agreements(next))) {
|
|
193
|
-
throw approvedOrderError('PRISM_PAYPAL_RECURRING_AGREEMENT_CHANGED', 'definitely_not_applied');
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
function readApprovedOrder(payload, owner, options) {
|
|
197
|
-
const order = record(payload);
|
|
198
|
-
const units = Array.isArray(order.purchase_units) ? order.purchase_units : [];
|
|
199
|
-
const unit = record(units[0]);
|
|
200
|
-
const payee = record(unit.payee);
|
|
201
|
-
if (owner.credentialBindingId !== options.credentialBindingId ||
|
|
202
|
-
owner.providerAccountId !== options.providerAccountId ||
|
|
203
|
-
owner.livemode !== (options.environment === 'live') ||
|
|
204
|
-
order.id !== owner.connectorPaymentId ||
|
|
205
|
-
order.intent !== 'CAPTURE' ||
|
|
206
|
-
units.length !== 1 ||
|
|
207
|
-
unit.reference_id !== owner.rootOperationId ||
|
|
208
|
-
unit.invoice_id !== owner.rootOperationId ||
|
|
209
|
-
(owner.expectedCustomId !== undefined && unit.custom_id !== owner.expectedCustomId) ||
|
|
210
|
-
(options.payerId !== undefined && payee.merchant_id !== options.payerId)) {
|
|
211
|
-
throw approvedOrderError('PRISM_PAYPAL_ORDER_OWNER');
|
|
212
|
-
}
|
|
213
|
-
const money = moneyFrom(unit.amount);
|
|
214
|
-
const suppliedBreakdown = record(unit.amount).breakdown;
|
|
215
|
-
const rawBreakdown = record(suppliedBreakdown);
|
|
216
|
-
if ((suppliedBreakdown !== undefined &&
|
|
217
|
-
(suppliedBreakdown === null ||
|
|
218
|
-
typeof suppliedBreakdown !== 'object' ||
|
|
219
|
-
Array.isArray(suppliedBreakdown))) ||
|
|
220
|
-
Object.keys(rawBreakdown).some((field) => !Object.values(breakdownFields).some((known) => known === field)) ||
|
|
221
|
-
(unit.items !== undefined && !Array.isArray(unit.items))) {
|
|
222
|
-
throw approvedOrderError('PRISM_PAYPAL_ORDER_RESPONSE');
|
|
223
|
-
}
|
|
224
|
-
const breakdown = {};
|
|
225
|
-
for (const [key, field] of Object.entries(breakdownFields)) {
|
|
226
|
-
if (rawBreakdown[field] !== undefined)
|
|
227
|
-
breakdown[key] = moneyFrom(rawBreakdown[field], money.currency).amountMinor;
|
|
228
|
-
}
|
|
229
|
-
const items = Array.isArray(unit.items)
|
|
230
|
-
? unit.items.map((value) => {
|
|
231
|
-
const item = record(value);
|
|
232
|
-
return {
|
|
233
|
-
name: text(item.name),
|
|
234
|
-
quantity: Number(item.quantity),
|
|
235
|
-
unitAmountMinor: moneyFrom(item.unit_amount, money.currency).amountMinor,
|
|
236
|
-
...(item.sku === undefined ? {} : { sku: text(item.sku) }),
|
|
237
|
-
...(item.category === undefined
|
|
238
|
-
? {}
|
|
239
|
-
: { category: text(item.category) }),
|
|
240
|
-
...(item.tax === undefined
|
|
241
|
-
? {}
|
|
242
|
-
: { taxMinor: moneyFrom(item.tax, money.currency).amountMinor }),
|
|
243
|
-
...(item.billing_plan === undefined
|
|
244
|
-
? {}
|
|
245
|
-
: { recurringAgreement: recurringFrom(item.billing_plan, money.currency) }),
|
|
246
|
-
};
|
|
247
|
-
})
|
|
248
|
-
: undefined;
|
|
249
|
-
const quote = {
|
|
250
|
-
money,
|
|
251
|
-
...(Object.keys(rawBreakdown).length ? { breakdown } : {}),
|
|
252
|
-
...(items ? { items } : {}),
|
|
253
|
-
};
|
|
254
|
-
paypalQuotePayload(quote);
|
|
255
|
-
const payments = record(unit.payments);
|
|
256
|
-
if ((unit.payments !== undefined &&
|
|
257
|
-
(unit.payments === null ||
|
|
258
|
-
typeof unit.payments !== 'object' ||
|
|
259
|
-
Array.isArray(unit.payments))) ||
|
|
260
|
-
(payments.captures !== undefined && !Array.isArray(payments.captures)) ||
|
|
261
|
-
(payments.authorizations !== undefined && !Array.isArray(payments.authorizations))) {
|
|
262
|
-
throw approvedOrderError('PRISM_PAYPAL_ORDER_RESPONSE');
|
|
263
|
-
}
|
|
264
|
-
const captures = Array.isArray(payments.captures) ? payments.captures : [];
|
|
265
|
-
const authorizations = Array.isArray(payments.authorizations) ? payments.authorizations : [];
|
|
266
|
-
if (captures.length > 1)
|
|
267
|
-
throw approvedOrderError('PRISM_PAYPAL_MULTIPLE_CAPTURES');
|
|
268
|
-
const statuses = [
|
|
269
|
-
'CREATED',
|
|
270
|
-
'SAVED',
|
|
271
|
-
'PAYER_ACTION_REQUIRED',
|
|
272
|
-
'APPROVED',
|
|
273
|
-
'VOIDED',
|
|
274
|
-
'COMPLETED',
|
|
275
|
-
];
|
|
276
|
-
const orderStatus = statuses.find((status) => status === order.status) ?? 'UNKNOWN';
|
|
277
|
-
let capture;
|
|
278
|
-
if (captures.length) {
|
|
279
|
-
const data = record(captures[0]);
|
|
280
|
-
const captureMoney = moneyFrom(data.amount, money.currency);
|
|
281
|
-
if (!text(data.id) ||
|
|
282
|
-
(data.invoice_id !== undefined && data.invoice_id !== owner.rootOperationId) ||
|
|
283
|
-
captureMoney.amountMinor !== money.amountMinor) {
|
|
284
|
-
throw approvedOrderError('PRISM_PAYPAL_CAPTURE_MISMATCH');
|
|
285
|
-
}
|
|
286
|
-
const captureStatuses = {
|
|
287
|
-
COMPLETED: 'captured',
|
|
288
|
-
PENDING: 'pending',
|
|
289
|
-
DECLINED: 'failed',
|
|
290
|
-
FAILED: 'failed',
|
|
291
|
-
DENIED: 'failed',
|
|
292
|
-
REFUNDED: 'refunded',
|
|
293
|
-
PARTIALLY_REFUNDED: 'partially_refunded',
|
|
294
|
-
};
|
|
295
|
-
capture = {
|
|
296
|
-
connectorCaptureId: text(data.id),
|
|
297
|
-
money: captureMoney,
|
|
298
|
-
status: captureStatuses[data.status] ?? 'unknown',
|
|
299
|
-
};
|
|
300
|
-
}
|
|
301
|
-
return {
|
|
302
|
-
connectorPaymentId: owner.connectorPaymentId,
|
|
303
|
-
rootOperationId: owner.rootOperationId,
|
|
304
|
-
quote,
|
|
305
|
-
orderStatus,
|
|
306
|
-
hasAuthorization: authorizations.length > 0,
|
|
307
|
-
hasCapture: captures.length > 0,
|
|
308
|
-
...(capture
|
|
309
|
-
? {
|
|
310
|
-
capture,
|
|
311
|
-
connectorMetadata: JSON.stringify({
|
|
312
|
-
authorize_id: null,
|
|
313
|
-
capture_id: capture.connectorCaptureId,
|
|
314
|
-
incremental_authorization_id: null,
|
|
315
|
-
psync_flow: 'CAPTURE',
|
|
316
|
-
next_action: null,
|
|
317
|
-
order_id: null,
|
|
318
|
-
}),
|
|
319
|
-
}
|
|
320
|
-
: {}),
|
|
321
|
-
...(typeof order.update_time === 'string' && Number.isFinite(Date.parse(order.update_time))
|
|
322
|
-
? { providerUpdatedAt: order.update_time }
|
|
323
|
-
: {}),
|
|
324
|
-
};
|
|
325
|
-
}
|
|
326
|
-
//# sourceMappingURL=paypal-approved-order.js.map
|