@adminiumjs/add-on-contracts 0.3.31 → 0.3.33
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/add-on-block.d.ts +4 -0
- package/dist/add-on-block.d.ts.map +1 -1
- package/dist/contracts.d.ts +6 -0
- package/dist/contracts.d.ts.map +1 -1
- package/dist/contracts.js +16 -0
- package/dist/contracts.js.map +1 -1
- package/dist/document-render.d.ts +9 -2
- package/dist/document-render.d.ts.map +1 -1
- package/dist/document-render.js +4 -2
- package/dist/document-render.js.map +1 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/payment-demo.d.ts +115 -0
- package/dist/payment-demo.d.ts.map +1 -0
- package/dist/payment-demo.js +659 -0
- package/dist/payment-demo.js.map +1 -0
- package/dist/payment-money.d.ts +29 -0
- package/dist/payment-money.d.ts.map +1 -0
- package/dist/payment-money.js +99 -0
- package/dist/payment-money.js.map +1 -0
- package/dist/payment-provider.d.ts +677 -0
- package/dist/payment-provider.d.ts.map +1 -0
- package/dist/payment-provider.js +260 -0
- package/dist/payment-provider.js.map +1 -0
- package/dist/runtime/app-types.d.ts +17 -0
- package/dist/runtime/app-types.d.ts.map +1 -1
- package/dist/runtime/app.d.ts +2 -0
- package/dist/runtime/app.d.ts.map +1 -1
- package/dist/runtime/app.js +2 -0
- package/dist/runtime/app.js.map +1 -1
- package/dist/runtime/index.d.ts +2 -2
- package/dist/runtime/index.d.ts.map +1 -1
- package/dist/runtime/index.js +2 -1
- package/dist/runtime/index.js.map +1 -1
- package/dist/testing/index.d.ts +1 -0
- package/dist/testing/index.d.ts.map +1 -1
- package/dist/testing/index.js +1 -0
- package/dist/testing/index.js.map +1 -1
- package/dist/testing/payment-provider.d.ts +43 -0
- package/dist/testing/payment-provider.d.ts.map +1 -0
- package/dist/testing/payment-provider.js +308 -0
- package/dist/testing/payment-provider.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { type CheckoutInput, type PaymentCredentials, type PaymentMode, type PaymentProvider, type WebhookDelivery } from '../payment-provider.js';
|
|
2
|
+
/** One amount the suite converts: exactly, or refused. */
|
|
3
|
+
export interface PaymentAmountCase {
|
|
4
|
+
amount: string;
|
|
5
|
+
currency: string;
|
|
6
|
+
/** What the provider must do with it. */
|
|
7
|
+
expect: 'exact' | 'refused';
|
|
8
|
+
}
|
|
9
|
+
/** The customer and the provider's back office, as far as this provider's test tools reach. */
|
|
10
|
+
export interface PaymentConformanceDriver {
|
|
11
|
+
/** Pays a checkout. Required for every case past "a checkout can be made". */
|
|
12
|
+
pay?(checkoutRef: string, outcome: 'succeeds' | 'fails'): Promise<void>;
|
|
13
|
+
/** Pays with a method whose money arrives later. */
|
|
14
|
+
payLate?(checkoutRef: string): Promise<void>;
|
|
15
|
+
/** Makes a late payment arrive now. */
|
|
16
|
+
settle?(checkoutRef: string): Promise<void>;
|
|
17
|
+
/** Makes a refund that succeeded fail afterwards. */
|
|
18
|
+
failRefund?(refundRef: string): Promise<void>;
|
|
19
|
+
/** A genuine signed delivery of the newest event about this checkout, or null when none can be had. */
|
|
20
|
+
latestDelivery?(checkoutRef: string): Promise<WebhookDelivery | null>;
|
|
21
|
+
}
|
|
22
|
+
export interface PaymentConformanceOptions {
|
|
23
|
+
/** A label for the suite's describe block. */
|
|
24
|
+
name: string;
|
|
25
|
+
/** Builds a provider for one operation, as the engine does. */
|
|
26
|
+
make: () => PaymentProvider;
|
|
27
|
+
/** The credential values handed to `make`, so the suite can check none leaks. */
|
|
28
|
+
credentials: PaymentCredentials;
|
|
29
|
+
mode: PaymentMode;
|
|
30
|
+
driver: PaymentConformanceDriver;
|
|
31
|
+
/** A payable the provider takes (its currency must be one it supports). */
|
|
32
|
+
payable: CheckoutInput['payable'];
|
|
33
|
+
methods: readonly string[];
|
|
34
|
+
amounts: readonly PaymentAmountCase[];
|
|
35
|
+
/** Must be https URLs the provider accepts as return addresses. */
|
|
36
|
+
successUrl: string;
|
|
37
|
+
cancelUrl: string;
|
|
38
|
+
}
|
|
39
|
+
/** The standard amount table: the currencies the plan names, both ways. */
|
|
40
|
+
export declare const PAYMENT_AMOUNT_CASES: readonly PaymentAmountCase[];
|
|
41
|
+
/** Registers the suite's tests for one provider. Call it inside a test file. */
|
|
42
|
+
export declare function paymentProviderConformance(options: PaymentConformanceOptions): void;
|
|
43
|
+
//# sourceMappingURL=payment-provider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"payment-provider.d.ts","sourceRoot":"","sources":["../../src/testing/payment-provider.ts"],"names":[],"mappings":"AAuBA,OAAO,EAIL,KAAK,aAAa,EAElB,KAAK,kBAAkB,EACvB,KAAK,WAAW,EAChB,KAAK,eAAe,EAEpB,KAAK,eAAe,EACrB,MAAM,wBAAwB,CAAC;AAEhC,0DAA0D;AAC1D,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,yCAAyC;IACzC,MAAM,EAAE,OAAO,GAAG,SAAS,CAAC;CAC7B;AAED,+FAA+F;AAC/F,MAAM,WAAW,wBAAwB;IACvC,8EAA8E;IAC9E,GAAG,CAAC,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,GAAG,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACxE,oDAAoD;IACpD,OAAO,CAAC,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7C,uCAAuC;IACvC,MAAM,CAAC,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5C,qDAAqD;IACrD,UAAU,CAAC,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9C,uGAAuG;IACvG,cAAc,CAAC,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC,CAAC;CACvE;AAED,MAAM,WAAW,yBAAyB;IACxC,8CAA8C;IAC9C,IAAI,EAAE,MAAM,CAAC;IACb,+DAA+D;IAC/D,IAAI,EAAE,MAAM,eAAe,CAAC;IAC5B,iFAAiF;IACjF,WAAW,EAAE,kBAAkB,CAAC;IAChC,IAAI,EAAE,WAAW,CAAC;IAClB,MAAM,EAAE,wBAAwB,CAAC;IACjC,2EAA2E;IAC3E,OAAO,EAAE,aAAa,CAAC,SAAS,CAAC,CAAC;IAClC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IAC3B,OAAO,EAAE,SAAS,iBAAiB,EAAE,CAAC;IACtC,mEAAmE;IACnE,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,2EAA2E;AAC3E,eAAO,MAAM,oBAAoB,EAAE,SAAS,iBAAiB,EAQ5D,CAAC;AAuBF,gFAAgF;AAChF,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,yBAAyB,GAAG,IAAI,CAoSnF"}
|
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-only
|
|
2
|
+
/**
|
|
3
|
+
* The `payment-provider@1` conformance suite.
|
|
4
|
+
*
|
|
5
|
+
* Every provider runs it against itself: the demo provider in this package,
|
|
6
|
+
* and each real one in its own add-on (against the provider's mock server).
|
|
7
|
+
* A DRIVER plays the customer and the provider's back office, because only the
|
|
8
|
+
* provider's own test tools can pay a checkout or fail a refund. A driver that
|
|
9
|
+
* cannot do something leaves that ability out, and the cases that need it are
|
|
10
|
+
* skipped — by name, so a skipped case is visible rather than passed.
|
|
11
|
+
*
|
|
12
|
+
* What it holds a provider to, whatever the driver can do:
|
|
13
|
+
* - every answer parses with the contract's schema, and a refusal is data;
|
|
14
|
+
* - an unknown reference is `NOT_FOUND`, never a throw;
|
|
15
|
+
* - the same idempotency key gives the same checkout and the same refund;
|
|
16
|
+
* - a success says what was taken and carries the provider's payment id;
|
|
17
|
+
* - an amount is converted exactly or refused, never rounded;
|
|
18
|
+
* - a forged or altered webhook is refused;
|
|
19
|
+
* - no credential value appears in any answer.
|
|
20
|
+
*/
|
|
21
|
+
import { describe, expect, it } from 'vitest';
|
|
22
|
+
import { sameAmount } from '../payment-money.js';
|
|
23
|
+
import { PAYMENT_ANSWER_SCHEMAS, isProviderError, providerErrorSchema, } from '../payment-provider.js';
|
|
24
|
+
/** The standard amount table: the currencies the plan names, both ways. */
|
|
25
|
+
export const PAYMENT_AMOUNT_CASES = [
|
|
26
|
+
{ amount: '120.00', currency: 'EUR', expect: 'exact' },
|
|
27
|
+
{ amount: '120.005', currency: 'EUR', expect: 'refused' },
|
|
28
|
+
{ amount: '12000', currency: 'JPY', expect: 'exact' },
|
|
29
|
+
{ amount: '12000.5', currency: 'JPY', expect: 'refused' },
|
|
30
|
+
{ amount: '45.250', currency: 'KWD', expect: 'exact' },
|
|
31
|
+
{ amount: '45.2505', currency: 'KWD', expect: 'refused' },
|
|
32
|
+
{ amount: '0.30', currency: 'EUR', expect: 'exact' },
|
|
33
|
+
];
|
|
34
|
+
let sequence = 0;
|
|
35
|
+
/** A fresh idempotency key per call site; never reused across cases by accident. */
|
|
36
|
+
function freshKey(label) {
|
|
37
|
+
sequence += 1;
|
|
38
|
+
return `conformance-${label}-${Date.now().toString(36)}-${sequence}`;
|
|
39
|
+
}
|
|
40
|
+
function parsesAs(call, answer) {
|
|
41
|
+
if (isProviderError(answer)) {
|
|
42
|
+
const parsed = providerErrorSchema.safeParse(answer);
|
|
43
|
+
return parsed.success ? [] : parsed.error.issues.map((issue) => `${issue.path.join('.')}: ${issue.message}`);
|
|
44
|
+
}
|
|
45
|
+
const parsed = PAYMENT_ANSWER_SCHEMAS[call].safeParse(answer);
|
|
46
|
+
return parsed.success ? [] : parsed.error.issues.map((issue) => `${issue.path.join('.')}: ${issue.message}`);
|
|
47
|
+
}
|
|
48
|
+
function ok(answer, what) {
|
|
49
|
+
if (isProviderError(answer))
|
|
50
|
+
throw new Error(`${what} was refused: ${answer.code} ${answer.providerMessage}`);
|
|
51
|
+
return answer;
|
|
52
|
+
}
|
|
53
|
+
/** Registers the suite's tests for one provider. Call it inside a test file. */
|
|
54
|
+
export function paymentProviderConformance(options) {
|
|
55
|
+
const { driver } = options;
|
|
56
|
+
const secrets = Object.values(options.credentials).filter((value) => value.length >= 6);
|
|
57
|
+
function leaks(answer) {
|
|
58
|
+
const text = JSON.stringify(answer);
|
|
59
|
+
return secrets.filter((secret) => text.includes(secret)).map(() => 'a credential value appears in an answer');
|
|
60
|
+
}
|
|
61
|
+
async function checkout(label) {
|
|
62
|
+
const attemptId = freshKey(`attempt-${label}`);
|
|
63
|
+
const made = ok(await options.make().createCheckout({
|
|
64
|
+
payable: options.payable,
|
|
65
|
+
methods: options.methods,
|
|
66
|
+
successUrl: options.successUrl,
|
|
67
|
+
cancelUrl: options.cancelUrl,
|
|
68
|
+
attemptId,
|
|
69
|
+
idempotencyKey: freshKey(label),
|
|
70
|
+
}), 'createCheckout');
|
|
71
|
+
return { ref: made.ref, attemptId };
|
|
72
|
+
}
|
|
73
|
+
async function paid(label) {
|
|
74
|
+
const made = await checkout(label);
|
|
75
|
+
await driver.pay(made.ref, 'succeeds');
|
|
76
|
+
const state = ok(await options.make().status(made.ref), 'status');
|
|
77
|
+
if (state.paymentRef === null || state.amount === null || state.currency === null) {
|
|
78
|
+
throw new Error('a succeeded checkout gave no payment id or amount');
|
|
79
|
+
}
|
|
80
|
+
return { ref: made.ref, paymentRef: state.paymentRef, amount: state.amount, currency: state.currency };
|
|
81
|
+
}
|
|
82
|
+
describe(`payment-provider@1 conformance — ${options.name} (${options.mode})`, () => {
|
|
83
|
+
it('is a factory product with every call', () => {
|
|
84
|
+
const provider = options.make();
|
|
85
|
+
expect(typeof provider.key).toBe('string');
|
|
86
|
+
expect(provider.mode).toBe(options.mode);
|
|
87
|
+
for (const call of [
|
|
88
|
+
'checkCredentials',
|
|
89
|
+
'createCheckout',
|
|
90
|
+
'status',
|
|
91
|
+
'complete',
|
|
92
|
+
'expire',
|
|
93
|
+
'refund',
|
|
94
|
+
'refundStatus',
|
|
95
|
+
'verifyWebhook',
|
|
96
|
+
'listMissedEvents',
|
|
97
|
+
'methodsFor',
|
|
98
|
+
'toMinor',
|
|
99
|
+
'fromMinor',
|
|
100
|
+
]) {
|
|
101
|
+
expect(typeof provider[call], call).toBe('function');
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
it('checks its credentials and names the account', async () => {
|
|
105
|
+
const answer = await options.make().checkCredentials();
|
|
106
|
+
expect(parsesAs('checkCredentials', answer)).toEqual([]);
|
|
107
|
+
const check = ok(answer, 'checkCredentials');
|
|
108
|
+
expect(check.livemode).toBe(options.mode === 'live');
|
|
109
|
+
expect(leaks(answer)).toEqual([]);
|
|
110
|
+
});
|
|
111
|
+
it('lists the ways to pay in the declared shape', async () => {
|
|
112
|
+
const answer = await options.make().methodsFor({
|
|
113
|
+
country: 'DE',
|
|
114
|
+
currency: options.payable.currency,
|
|
115
|
+
amount: options.payable.amount,
|
|
116
|
+
});
|
|
117
|
+
expect(parsesAs('methodsFor', answer)).toEqual([]);
|
|
118
|
+
});
|
|
119
|
+
it('makes a checkout with an https hosted page', async () => {
|
|
120
|
+
const answer = await options.make().createCheckout({
|
|
121
|
+
payable: options.payable,
|
|
122
|
+
methods: options.methods,
|
|
123
|
+
successUrl: options.successUrl,
|
|
124
|
+
cancelUrl: options.cancelUrl,
|
|
125
|
+
attemptId: freshKey('attempt'),
|
|
126
|
+
idempotencyKey: freshKey('create'),
|
|
127
|
+
});
|
|
128
|
+
expect(parsesAs('createCheckout', answer)).toEqual([]);
|
|
129
|
+
expect(ok(answer, 'createCheckout').url.startsWith('https://')).toBe(true);
|
|
130
|
+
expect(leaks(answer)).toEqual([]);
|
|
131
|
+
});
|
|
132
|
+
it('gives the same checkout for the same idempotency key', async () => {
|
|
133
|
+
const key = freshKey('same');
|
|
134
|
+
const input = {
|
|
135
|
+
payable: options.payable,
|
|
136
|
+
methods: options.methods,
|
|
137
|
+
successUrl: options.successUrl,
|
|
138
|
+
cancelUrl: options.cancelUrl,
|
|
139
|
+
attemptId: freshKey('attempt'),
|
|
140
|
+
idempotencyKey: key,
|
|
141
|
+
};
|
|
142
|
+
const first = ok(await options.make().createCheckout(input), 'createCheckout');
|
|
143
|
+
const second = ok(await options.make().createCheckout(input), 'createCheckout again');
|
|
144
|
+
expect(second.ref).toBe(first.ref);
|
|
145
|
+
});
|
|
146
|
+
it('reads a new checkout as not paid, carrying the attempt id', async () => {
|
|
147
|
+
const made = await checkout('fresh');
|
|
148
|
+
const answer = await options.make().status(made.ref);
|
|
149
|
+
expect(parsesAs('status', answer)).toEqual([]);
|
|
150
|
+
const state = ok(answer, 'status');
|
|
151
|
+
expect(['pending', 'requires_action']).toContain(state.status);
|
|
152
|
+
expect(state.attemptId).toBe(made.attemptId);
|
|
153
|
+
expect(state.amount).toBeNull();
|
|
154
|
+
});
|
|
155
|
+
it('answers an unknown reference with NOT_FOUND, as data', async () => {
|
|
156
|
+
const answer = await options.make().status('no-such-checkout-0000');
|
|
157
|
+
expect(isProviderError(answer)).toBe(true);
|
|
158
|
+
expect(answer.code).toBe('NOT_FOUND');
|
|
159
|
+
expect(parsesAs('status', answer)).toEqual([]);
|
|
160
|
+
const refund = await options.make().refundStatus('no-such-refund-0000');
|
|
161
|
+
expect(refund.code).toBe('NOT_FOUND');
|
|
162
|
+
});
|
|
163
|
+
it('ends an open checkout', async () => {
|
|
164
|
+
const made = await checkout('expire');
|
|
165
|
+
const answer = await options.make().expire(made.ref);
|
|
166
|
+
expect(parsesAs('expire', answer)).toEqual([]);
|
|
167
|
+
expect(ok(answer, 'expire').status).toBe('expired');
|
|
168
|
+
expect(ok(await options.make().status(made.ref), 'status').status).toBe('expired');
|
|
169
|
+
});
|
|
170
|
+
it.skipIf(driver.pay === undefined)('reads a success with what was taken and the payment id', async () => {
|
|
171
|
+
const made = await checkout('pay');
|
|
172
|
+
await driver.pay(made.ref, 'succeeds');
|
|
173
|
+
const answer = await options.make().status(made.ref);
|
|
174
|
+
expect(parsesAs('status', answer)).toEqual([]);
|
|
175
|
+
const state = ok(answer, 'status');
|
|
176
|
+
expect(state.status).toBe('succeeded');
|
|
177
|
+
expect(state.livemode).toBe(options.mode === 'live');
|
|
178
|
+
expect(state.paymentRef).not.toBeNull();
|
|
179
|
+
expect(sameAmount(state.amount ?? '', options.payable.amount)).toBe(true);
|
|
180
|
+
expect(state.currency).toBe(options.payable.currency);
|
|
181
|
+
expect(leaks(answer)).toEqual([]);
|
|
182
|
+
});
|
|
183
|
+
it.skipIf(driver.pay === undefined)('completes a paid checkout as read (a no-op or a capture)', async () => {
|
|
184
|
+
const made = await checkout('complete');
|
|
185
|
+
await driver.pay(made.ref, 'succeeds');
|
|
186
|
+
const answer = await options.make().complete(made.ref, freshKey('complete'));
|
|
187
|
+
expect(parsesAs('complete', answer)).toEqual([]);
|
|
188
|
+
expect(ok(answer, 'complete').status).toBe('succeeded');
|
|
189
|
+
});
|
|
190
|
+
it.skipIf(driver.pay === undefined)('refuses to end a checkout that already completed', async () => {
|
|
191
|
+
const made = await checkout('expire-paid');
|
|
192
|
+
await driver.pay(made.ref, 'succeeds');
|
|
193
|
+
const answer = await options.make().expire(made.ref);
|
|
194
|
+
expect(isProviderError(answer) ? answer.code : 'no refusal').toBe('ALREADY_COMPLETED');
|
|
195
|
+
});
|
|
196
|
+
it.skipIf(driver.pay === undefined)('reads a declined payment as failed, with nothing taken', async () => {
|
|
197
|
+
const made = await checkout('decline');
|
|
198
|
+
await driver.pay(made.ref, 'fails');
|
|
199
|
+
const state = ok(await options.make().status(made.ref), 'status');
|
|
200
|
+
expect(state.status).toBe('failed');
|
|
201
|
+
expect(state.amount).toBeNull();
|
|
202
|
+
});
|
|
203
|
+
it.skipIf(driver.payLate === undefined || driver.settle === undefined)('reads a late payment as processing, then succeeded', async () => {
|
|
204
|
+
const made = await checkout('late');
|
|
205
|
+
await driver.payLate(made.ref);
|
|
206
|
+
const waiting = ok(await options.make().status(made.ref), 'status');
|
|
207
|
+
expect(waiting.status).toBe('processing');
|
|
208
|
+
expect(waiting.paidWith?.delayed).toBe(true);
|
|
209
|
+
await driver.settle(made.ref);
|
|
210
|
+
expect(ok(await options.make().status(made.ref), 'status').status).toBe('succeeded');
|
|
211
|
+
});
|
|
212
|
+
it.skipIf(driver.pay === undefined)('refunds in parts, never past what was taken', async () => {
|
|
213
|
+
const payment = await paid('refund');
|
|
214
|
+
const half = options.payable.currency === 'JPY' ? '1' : '0.01';
|
|
215
|
+
const first = await options.make().refund({
|
|
216
|
+
paymentRef: payment.paymentRef,
|
|
217
|
+
amount: half,
|
|
218
|
+
currency: payment.currency,
|
|
219
|
+
reason: 'requested_by_customer',
|
|
220
|
+
idempotencyKey: freshKey('refund'),
|
|
221
|
+
});
|
|
222
|
+
expect(parsesAs('refund', first)).toEqual([]);
|
|
223
|
+
expect(ok(first, 'refund').paymentRef).toBe(payment.paymentRef);
|
|
224
|
+
const over = await options.make().refund({
|
|
225
|
+
paymentRef: payment.paymentRef,
|
|
226
|
+
amount: payment.amount,
|
|
227
|
+
currency: payment.currency,
|
|
228
|
+
reason: 'requested_by_customer',
|
|
229
|
+
idempotencyKey: freshKey('refund-over'),
|
|
230
|
+
});
|
|
231
|
+
expect(isProviderError(over) ? over.code : 'no refusal').toBe('REFUND_REFUSED');
|
|
232
|
+
});
|
|
233
|
+
it.skipIf(driver.pay === undefined)('gives the same refund for the same idempotency key', async () => {
|
|
234
|
+
const payment = await paid('refund-twice');
|
|
235
|
+
const input = {
|
|
236
|
+
paymentRef: payment.paymentRef,
|
|
237
|
+
amount: payment.amount,
|
|
238
|
+
currency: payment.currency,
|
|
239
|
+
reason: 'duplicate',
|
|
240
|
+
idempotencyKey: freshKey('refund-same'),
|
|
241
|
+
};
|
|
242
|
+
const first = ok(await options.make().refund(input), 'refund');
|
|
243
|
+
const second = ok(await options.make().refund(input), 'refund again');
|
|
244
|
+
expect(second.ref).toBe(first.ref);
|
|
245
|
+
const read = await options.make().refundStatus(first.ref);
|
|
246
|
+
expect(parsesAs('refundStatus', read)).toEqual([]);
|
|
247
|
+
});
|
|
248
|
+
it.skipIf(driver.pay === undefined || driver.failRefund === undefined)('reads a refund that fails after it succeeded', async () => {
|
|
249
|
+
const payment = await paid('refund-fail');
|
|
250
|
+
const refund = ok(await options.make().refund({
|
|
251
|
+
paymentRef: payment.paymentRef,
|
|
252
|
+
amount: payment.amount,
|
|
253
|
+
currency: payment.currency,
|
|
254
|
+
reason: 'requested_by_customer',
|
|
255
|
+
idempotencyKey: freshKey('refund-fail'),
|
|
256
|
+
}), 'refund');
|
|
257
|
+
await driver.failRefund(refund.ref);
|
|
258
|
+
const read = ok(await options.make().refundStatus(refund.ref), 'refundStatus');
|
|
259
|
+
expect(read.status).toBe('failed');
|
|
260
|
+
expect(read.failureMessage).not.toBeNull();
|
|
261
|
+
});
|
|
262
|
+
it.skipIf(driver.pay === undefined || driver.latestDelivery === undefined)('verifies a genuine webhook and refuses a forged or altered one', async () => {
|
|
263
|
+
const made = await checkout('webhook');
|
|
264
|
+
await driver.pay(made.ref, 'succeeds');
|
|
265
|
+
const delivery = await driver.latestDelivery(made.ref);
|
|
266
|
+
expect(delivery, 'the driver gave no delivery').not.toBeNull();
|
|
267
|
+
const genuine = await options.make().verifyWebhook(delivery);
|
|
268
|
+
expect(parsesAs('verifyWebhook', genuine)).toEqual([]);
|
|
269
|
+
const event = ok(genuine, 'verifyWebhook');
|
|
270
|
+
expect(event.checkoutRef).toBe(made.ref);
|
|
271
|
+
expect(event.livemode).toBe(options.mode === 'live');
|
|
272
|
+
const altered = await options.make().verifyWebhook({
|
|
273
|
+
...delivery,
|
|
274
|
+
rawBody: delivery.rawBody.replace(made.ref, `${made.ref}x`),
|
|
275
|
+
});
|
|
276
|
+
expect(isProviderError(altered) ? altered.code : 'accepted').toBe('SIGNATURE_INVALID');
|
|
277
|
+
const unsigned = await options.make().verifyWebhook({ rawBody: delivery.rawBody, headers: {} });
|
|
278
|
+
expect(isProviderError(unsigned) ? unsigned.code : 'accepted').toBe('SIGNATURE_INVALID');
|
|
279
|
+
});
|
|
280
|
+
it('lists missed events in the declared shape, moving its cursor forward', async () => {
|
|
281
|
+
const first = await options.make().listMissedEvents(null);
|
|
282
|
+
expect(parsesAs('listMissedEvents', first)).toEqual([]);
|
|
283
|
+
const page = ok(first, 'listMissedEvents');
|
|
284
|
+
const next = await options.make().listMissedEvents(page.cursor);
|
|
285
|
+
expect(parsesAs('listMissedEvents', next)).toEqual([]);
|
|
286
|
+
const seen = new Set(page.events.map((one) => one.eventId));
|
|
287
|
+
const repeated = ok(next, 'listMissedEvents again').events.filter((one) => seen.has(one.eventId));
|
|
288
|
+
expect(repeated.map((one) => one.eventId)).toEqual([]);
|
|
289
|
+
});
|
|
290
|
+
describe('converts amounts exactly or refuses them', () => {
|
|
291
|
+
for (const one of options.amounts) {
|
|
292
|
+
it(`${one.amount} ${one.currency} is ${one.expect}`, () => {
|
|
293
|
+
const provider = options.make();
|
|
294
|
+
const minor = provider.toMinor(one.amount, one.currency);
|
|
295
|
+
if (one.expect === 'refused') {
|
|
296
|
+
expect(isProviderError(minor) ? minor.code : 'converted').toMatch(/^(AMOUNT_UNREPRESENTABLE|CURRENCY_UNSUPPORTED)$/);
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
299
|
+
const converted = ok(minor, 'toMinor');
|
|
300
|
+
expect(converted.minor).toMatch(/^(0|[1-9]\d*)$/);
|
|
301
|
+
const back = ok(provider.fromMinor(converted.minor, one.currency), 'fromMinor');
|
|
302
|
+
expect(sameAmount(back, one.amount), `${one.amount} came back as ${back}`).toBe(true);
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
});
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
//# sourceMappingURL=payment-provider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"payment-provider.js","sourceRoot":"","sources":["../../src/testing/payment-provider.ts"],"names":[],"mappings":"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"}
|