@commercejs/payment-tap 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/__tests__/tap-provider.test.d.ts +2 -0
- package/dist/__tests__/tap-provider.test.d.ts.map +1 -0
- package/dist/__tests__/tap-provider.test.js +259 -0
- package/dist/__tests__/tap-provider.test.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -0
- package/dist/tap-provider.d.ts +42 -0
- package/dist/tap-provider.d.ts.map +1 -0
- package/dist/tap-provider.js +230 -0
- package/dist/tap-provider.js.map +1 -0
- package/dist/types.d.ts +60 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +5 -0
- package/dist/types.js.map +1 -0
- package/package.json +32 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tap-provider.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/tap-provider.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
2
|
+
import { TapPaymentProvider } from '../tap-provider.js';
|
|
3
|
+
// ---------------------------------------------------------------------------
|
|
4
|
+
// Helpers
|
|
5
|
+
// ---------------------------------------------------------------------------
|
|
6
|
+
function makeTapCharge(overrides = {}) {
|
|
7
|
+
return {
|
|
8
|
+
id: 'chg_test_abc123',
|
|
9
|
+
status: 'INITIATED',
|
|
10
|
+
amount: 99.99,
|
|
11
|
+
currency: 'SAR',
|
|
12
|
+
threeDSecure: true,
|
|
13
|
+
source: { id: 'src_card', type: 'CARD_NOT_PRESENT' },
|
|
14
|
+
transaction: {
|
|
15
|
+
url: 'https://tap.company/3ds/abc123',
|
|
16
|
+
created: '2026-02-09T12:00:00Z',
|
|
17
|
+
},
|
|
18
|
+
redirect: { url: 'https://store.com/checkout/confirm' },
|
|
19
|
+
created: '2026-02-09T12:00:00Z',
|
|
20
|
+
...overrides,
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
function mockFetchResponse(data, status = 200) {
|
|
24
|
+
return vi.fn().mockResolvedValue({
|
|
25
|
+
ok: status >= 200 && status < 300,
|
|
26
|
+
status,
|
|
27
|
+
json: () => Promise.resolve(data),
|
|
28
|
+
text: () => Promise.resolve(JSON.stringify(data)),
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
// ---------------------------------------------------------------------------
|
|
32
|
+
// Tests
|
|
33
|
+
// ---------------------------------------------------------------------------
|
|
34
|
+
describe('TapPaymentProvider', () => {
|
|
35
|
+
let provider;
|
|
36
|
+
beforeEach(() => {
|
|
37
|
+
provider = new TapPaymentProvider({
|
|
38
|
+
secretKey: 'sk_test_xxx',
|
|
39
|
+
webhookSecret: 'whsec_test',
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
// ---- Constructor -------------------------------------------------------
|
|
43
|
+
describe('constructor', () => {
|
|
44
|
+
it('sets id and name', () => {
|
|
45
|
+
expect(provider.id).toBe('tap');
|
|
46
|
+
expect(provider.name).toBe('Tap Payments');
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
// ---- createSession ----------------------------------------------------
|
|
50
|
+
describe('createSession', () => {
|
|
51
|
+
it('creates a charge and returns a PaymentSession with redirectUrl', async () => {
|
|
52
|
+
const charge = makeTapCharge();
|
|
53
|
+
globalThis.fetch = mockFetchResponse(charge);
|
|
54
|
+
const session = await provider.createSession({
|
|
55
|
+
amount: 99.99,
|
|
56
|
+
currency: 'SAR',
|
|
57
|
+
sourceToken: 'tok_test_123',
|
|
58
|
+
returnUrl: 'https://store.com/checkout/confirm',
|
|
59
|
+
});
|
|
60
|
+
expect(session.id).toBe('chg_test_abc123');
|
|
61
|
+
expect(session.providerId).toBe('tap');
|
|
62
|
+
expect(session.status).toBe('pending');
|
|
63
|
+
expect(session.amount).toBe(99.99);
|
|
64
|
+
expect(session.currency).toBe('SAR');
|
|
65
|
+
expect(session.redirectUrl).toBe('https://tap.company/3ds/abc123');
|
|
66
|
+
// Verify correct API call
|
|
67
|
+
expect(globalThis.fetch).toHaveBeenCalledWith('https://api.tap.company/v2/charges', expect.objectContaining({
|
|
68
|
+
method: 'POST',
|
|
69
|
+
headers: expect.objectContaining({
|
|
70
|
+
'Authorization': 'Bearer sk_test_xxx',
|
|
71
|
+
}),
|
|
72
|
+
}));
|
|
73
|
+
});
|
|
74
|
+
it('passes orderId as reference.order', async () => {
|
|
75
|
+
const charge = makeTapCharge();
|
|
76
|
+
globalThis.fetch = mockFetchResponse(charge);
|
|
77
|
+
await provider.createSession({
|
|
78
|
+
amount: 50,
|
|
79
|
+
currency: 'SAR',
|
|
80
|
+
orderId: 'order-123',
|
|
81
|
+
returnUrl: 'https://store.com/confirm',
|
|
82
|
+
});
|
|
83
|
+
const callBody = JSON.parse(globalThis.fetch.mock.calls[0][1].body);
|
|
84
|
+
expect(callBody.reference).toEqual({ order: 'order-123' });
|
|
85
|
+
});
|
|
86
|
+
it('uses src_all when no sourceToken is provided', async () => {
|
|
87
|
+
const charge = makeTapCharge();
|
|
88
|
+
globalThis.fetch = mockFetchResponse(charge);
|
|
89
|
+
await provider.createSession({
|
|
90
|
+
amount: 10,
|
|
91
|
+
currency: 'SAR',
|
|
92
|
+
returnUrl: 'https://store.com/confirm',
|
|
93
|
+
});
|
|
94
|
+
const callBody = JSON.parse(globalThis.fetch.mock.calls[0][1].body);
|
|
95
|
+
expect(callBody.source).toEqual({ id: 'src_all' });
|
|
96
|
+
});
|
|
97
|
+
it('throws on Tap API error', async () => {
|
|
98
|
+
globalThis.fetch = mockFetchResponse({ error: 'invalid' }, 400);
|
|
99
|
+
await expect(provider.createSession({ amount: 10, currency: 'SAR' })).rejects.toThrow('Tap API error (400)');
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
// ---- confirmSession ---------------------------------------------------
|
|
103
|
+
describe('confirmSession', () => {
|
|
104
|
+
it('re-fetches charge to get final status after 3DS', async () => {
|
|
105
|
+
const captured = makeTapCharge({ status: 'CAPTURED' });
|
|
106
|
+
globalThis.fetch = mockFetchResponse(captured);
|
|
107
|
+
const session = await provider.confirmSession('chg_test_abc123');
|
|
108
|
+
expect(session.status).toBe('captured');
|
|
109
|
+
expect(globalThis.fetch).toHaveBeenCalledWith('https://api.tap.company/v2/charges/chg_test_abc123', expect.objectContaining({ method: 'GET' }));
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
// ---- getSession -------------------------------------------------------
|
|
113
|
+
describe('getSession', () => {
|
|
114
|
+
it('fetches charge by ID', async () => {
|
|
115
|
+
const charge = makeTapCharge({ status: 'IN_PROGRESS' });
|
|
116
|
+
globalThis.fetch = mockFetchResponse(charge);
|
|
117
|
+
const session = await provider.getSession('chg_test_abc123');
|
|
118
|
+
expect(session.status).toBe('processing');
|
|
119
|
+
expect(session.id).toBe('chg_test_abc123');
|
|
120
|
+
});
|
|
121
|
+
});
|
|
122
|
+
// ---- refund -----------------------------------------------------------
|
|
123
|
+
describe('refund', () => {
|
|
124
|
+
it('creates a refund and returns updated session', async () => {
|
|
125
|
+
const fetchMock = vi.fn()
|
|
126
|
+
// First call: getSession to get current amount/currency
|
|
127
|
+
.mockResolvedValueOnce({
|
|
128
|
+
ok: true, status: 200,
|
|
129
|
+
json: () => Promise.resolve(makeTapCharge({ status: 'CAPTURED' })),
|
|
130
|
+
text: () => Promise.resolve(''),
|
|
131
|
+
})
|
|
132
|
+
// Second call: POST /refunds
|
|
133
|
+
.mockResolvedValueOnce({
|
|
134
|
+
ok: true, status: 200,
|
|
135
|
+
json: () => Promise.resolve({ id: 'ref_123', status: 'REFUNDED' }),
|
|
136
|
+
text: () => Promise.resolve(''),
|
|
137
|
+
})
|
|
138
|
+
// Third call: getSession to get updated status
|
|
139
|
+
.mockResolvedValueOnce({
|
|
140
|
+
ok: true, status: 200,
|
|
141
|
+
json: () => Promise.resolve(makeTapCharge({ status: 'REFUNDED' })),
|
|
142
|
+
text: () => Promise.resolve(''),
|
|
143
|
+
});
|
|
144
|
+
globalThis.fetch = fetchMock;
|
|
145
|
+
const session = await provider.refund({
|
|
146
|
+
sessionId: 'chg_test_abc123',
|
|
147
|
+
reason: 'requested_by_customer',
|
|
148
|
+
});
|
|
149
|
+
expect(session.status).toBe('refunded');
|
|
150
|
+
expect(fetchMock).toHaveBeenCalledTimes(3);
|
|
151
|
+
// Verify refund POST body
|
|
152
|
+
const refundCall = fetchMock.mock.calls[1];
|
|
153
|
+
expect(refundCall[0]).toBe('https://api.tap.company/v2/refunds');
|
|
154
|
+
const refundBody = JSON.parse(refundCall[1].body);
|
|
155
|
+
expect(refundBody.charge_id).toBe('chg_test_abc123');
|
|
156
|
+
expect(refundBody.amount).toBe(99.99); // full refund
|
|
157
|
+
});
|
|
158
|
+
it('supports partial refund with explicit amount', async () => {
|
|
159
|
+
const fetchMock = vi.fn()
|
|
160
|
+
.mockResolvedValueOnce({
|
|
161
|
+
ok: true, status: 200,
|
|
162
|
+
json: () => Promise.resolve(makeTapCharge({ status: 'CAPTURED' })),
|
|
163
|
+
text: () => Promise.resolve(''),
|
|
164
|
+
})
|
|
165
|
+
.mockResolvedValueOnce({
|
|
166
|
+
ok: true, status: 200,
|
|
167
|
+
json: () => Promise.resolve({ id: 'ref_456', status: 'REFUNDED' }),
|
|
168
|
+
text: () => Promise.resolve(''),
|
|
169
|
+
})
|
|
170
|
+
.mockResolvedValueOnce({
|
|
171
|
+
ok: true, status: 200,
|
|
172
|
+
json: () => Promise.resolve(makeTapCharge({ status: 'CAPTURED' })),
|
|
173
|
+
text: () => Promise.resolve(''),
|
|
174
|
+
});
|
|
175
|
+
globalThis.fetch = fetchMock;
|
|
176
|
+
await provider.refund({
|
|
177
|
+
sessionId: 'chg_test_abc123',
|
|
178
|
+
amount: 25.00,
|
|
179
|
+
});
|
|
180
|
+
const refundBody = JSON.parse(fetchMock.mock.calls[1][1].body);
|
|
181
|
+
expect(refundBody.amount).toBe(25.00);
|
|
182
|
+
});
|
|
183
|
+
});
|
|
184
|
+
// ---- Status mapping ---------------------------------------------------
|
|
185
|
+
describe('status mapping', () => {
|
|
186
|
+
const cases = [
|
|
187
|
+
['INITIATED', 'pending'],
|
|
188
|
+
['IN_PROGRESS', 'processing'],
|
|
189
|
+
['CAPTURED', 'captured'],
|
|
190
|
+
['FAILED', 'failed'],
|
|
191
|
+
['DECLINED', 'failed'],
|
|
192
|
+
['RESTRICTED', 'failed'],
|
|
193
|
+
['VOID', 'cancelled'],
|
|
194
|
+
['CANCELLED', 'cancelled'],
|
|
195
|
+
['TIMEDOUT', 'cancelled'],
|
|
196
|
+
['ABANDONED', 'cancelled'],
|
|
197
|
+
['REFUNDED', 'refunded'],
|
|
198
|
+
];
|
|
199
|
+
it.each(cases)('maps Tap "%s" → "%s"', async (tapStatus, expected) => {
|
|
200
|
+
const charge = makeTapCharge({ status: tapStatus });
|
|
201
|
+
globalThis.fetch = mockFetchResponse(charge);
|
|
202
|
+
const session = await provider.getSession('chg_test_abc123');
|
|
203
|
+
expect(session.status).toBe(expected);
|
|
204
|
+
});
|
|
205
|
+
});
|
|
206
|
+
// ---- cancelSession ----------------------------------------------------
|
|
207
|
+
describe('cancelSession', () => {
|
|
208
|
+
it('returns current session state', async () => {
|
|
209
|
+
const charge = makeTapCharge({ status: 'CANCELLED' });
|
|
210
|
+
globalThis.fetch = mockFetchResponse(charge);
|
|
211
|
+
const session = await provider.cancelSession('chg_test_abc123');
|
|
212
|
+
expect(session.status).toBe('cancelled');
|
|
213
|
+
});
|
|
214
|
+
});
|
|
215
|
+
// ---- verifyWebhook ----------------------------------------------------
|
|
216
|
+
describe('verifyWebhook', () => {
|
|
217
|
+
it('throws on invalid hashstring', async () => {
|
|
218
|
+
const payload = JSON.stringify({
|
|
219
|
+
id: 'chg_test_abc123',
|
|
220
|
+
amount: 99.99,
|
|
221
|
+
currency: 'SAR',
|
|
222
|
+
status: 'CAPTURED',
|
|
223
|
+
reference: { gateway: 'gw_ref', payment: 'pay_ref' },
|
|
224
|
+
transaction: { created: '2026-02-09T12:00:00Z' },
|
|
225
|
+
});
|
|
226
|
+
await expect(provider.verifyWebhook(payload, 'invalid_sig')).rejects.toThrow('invalid webhook hashstring');
|
|
227
|
+
});
|
|
228
|
+
it('returns parsed event on valid hashstring', async () => {
|
|
229
|
+
const event = {
|
|
230
|
+
id: 'chg_test_abc123',
|
|
231
|
+
amount: 99.99,
|
|
232
|
+
currency: 'SAR',
|
|
233
|
+
status: 'CAPTURED',
|
|
234
|
+
reference: { gateway: 'gw_ref', payment: 'pay_ref' },
|
|
235
|
+
transaction: { created: '2026-02-09T12:00:00Z' },
|
|
236
|
+
};
|
|
237
|
+
const payload = JSON.stringify(event);
|
|
238
|
+
// Build the hashstring the same way the implementation does
|
|
239
|
+
const toBeHashed = `x_id${event.id}` +
|
|
240
|
+
`x_amount${event.amount}` +
|
|
241
|
+
`x_currency${event.currency}` +
|
|
242
|
+
`x_gateway_reference${event.reference.gateway}` +
|
|
243
|
+
`x_payment_reference${event.reference.payment}` +
|
|
244
|
+
`x_status${event.status}` +
|
|
245
|
+
`x_created${event.transaction.created}`;
|
|
246
|
+
// HMAC with secretKey (not webhookSecret — matches implementation)
|
|
247
|
+
const encoder = new TextEncoder();
|
|
248
|
+
const key = await globalThis.crypto.subtle.importKey('raw', encoder.encode('sk_test_xxx'), { name: 'HMAC', hash: 'SHA-256' }, false, ['sign']);
|
|
249
|
+
const sig = await globalThis.crypto.subtle.sign('HMAC', key, encoder.encode(toBeHashed));
|
|
250
|
+
const validSignature = Array.from(new Uint8Array(sig))
|
|
251
|
+
.map(b => b.toString(16).padStart(2, '0'))
|
|
252
|
+
.join('');
|
|
253
|
+
const result = await provider.verifyWebhook(payload, validSignature);
|
|
254
|
+
expect(result.type).toBe('payment.captured');
|
|
255
|
+
expect(result.sessionId).toBe('chg_test_abc123');
|
|
256
|
+
});
|
|
257
|
+
});
|
|
258
|
+
});
|
|
259
|
+
//# sourceMappingURL=tap-provider.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tap-provider.test.js","sourceRoot":"","sources":["../../src/__tests__/tap-provider.test.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAA;AACtD,YAAY,EAAE,SAAS,EAAE,YAAY,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// @commercejs/payment-tap — Tap Payments provider for CommerceJS
|
|
3
|
+
// ---------------------------------------------------------------------------
|
|
4
|
+
export { TapPaymentProvider } from './tap-provider.js';
|
|
5
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,iEAAiE;AACjE,8EAA8E;AAE9E,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAA"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { PaymentProvider, PaymentSession, CreatePaymentSessionInput, RefundInput, PaymentWebhookEvent } from '@commercejs/types';
|
|
2
|
+
import type { TapConfig } from './types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Tap Payments provider — redirect-based, PCI-free.
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* ```ts
|
|
8
|
+
* const tap = new TapPaymentProvider({ secretKey: 'sk_test_...' })
|
|
9
|
+
*
|
|
10
|
+
* // 1. Create charge with token from goSell.js
|
|
11
|
+
* const session = await tap.createSession({
|
|
12
|
+
* amount: 99.99,
|
|
13
|
+
* currency: 'SAR',
|
|
14
|
+
* sourceToken: 'tok_xxx',
|
|
15
|
+
* returnUrl: 'https://store.com/checkout/confirm',
|
|
16
|
+
* })
|
|
17
|
+
*
|
|
18
|
+
* // 2. Redirect customer to session.redirectUrl for 3DS
|
|
19
|
+
*
|
|
20
|
+
* // 3. After redirect, confirm the payment
|
|
21
|
+
* const confirmed = await tap.confirmSession(session.id)
|
|
22
|
+
* // confirmed.status === 'captured'
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
export declare class TapPaymentProvider implements PaymentProvider {
|
|
26
|
+
readonly id = "tap";
|
|
27
|
+
readonly name = "Tap Payments";
|
|
28
|
+
private readonly secretKey;
|
|
29
|
+
private readonly baseUrl;
|
|
30
|
+
private readonly webhookSecret;
|
|
31
|
+
constructor(config: TapConfig);
|
|
32
|
+
createSession(input: CreatePaymentSessionInput): Promise<PaymentSession>;
|
|
33
|
+
confirmSession(sessionId: string): Promise<PaymentSession>;
|
|
34
|
+
getSession(sessionId: string): Promise<PaymentSession>;
|
|
35
|
+
refund(input: RefundInput): Promise<PaymentSession>;
|
|
36
|
+
cancelSession(sessionId: string): Promise<PaymentSession>;
|
|
37
|
+
verifyWebhook(payload: string | Uint8Array, signature: string): Promise<PaymentWebhookEvent>;
|
|
38
|
+
/** Map Tap charge status to a webhook event type */
|
|
39
|
+
private mapChargeStatusToEventType;
|
|
40
|
+
private request;
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=tap-provider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tap-provider.d.ts","sourceRoot":"","sources":["../src/tap-provider.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EACV,eAAe,EACf,cAAc,EAEd,yBAAyB,EACzB,WAAW,EACX,mBAAmB,EACpB,MAAM,mBAAmB,CAAA;AAE1B,OAAO,KAAK,EAAE,SAAS,EAAiC,MAAM,YAAY,CAAA;AAgD1E;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,qBAAa,kBAAmB,YAAW,eAAe;IACxD,QAAQ,CAAC,EAAE,SAAQ;IACnB,QAAQ,CAAC,IAAI,kBAAiB;IAE9B,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAQ;IAClC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAQ;IAChC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAe;gBAEjC,MAAM,EAAE,SAAS;IAUvB,aAAa,CAAC,KAAK,EAAE,yBAAyB,GAAG,OAAO,CAAC,cAAc,CAAC;IA+BxE,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;IAK1D,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;IAKtD,MAAM,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,cAAc,CAAC;IAmBnD,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC;IAOzD,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,UAAU,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,CAAC;IA6DlG,oDAAoD;IACpD,OAAO,CAAC,0BAA0B;YAmBpB,OAAO;CAqBtB"}
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// TapPaymentProvider — Tap Payments gateway (redirect-based, PCI-free)
|
|
3
|
+
// ---------------------------------------------------------------------------
|
|
4
|
+
//
|
|
5
|
+
// Flow: tokenize client-side (goSell.js) → create charge server-side →
|
|
6
|
+
// redirect for 3DS → confirm after redirect → done.
|
|
7
|
+
//
|
|
8
|
+
// Tap auto-captures charges, so captureSession is not implemented.
|
|
9
|
+
// ---------------------------------------------------------------------------
|
|
10
|
+
const TAP_API_BASE = 'https://api.tap.company/v2';
|
|
11
|
+
/** Map Tap charge status → PaymentSessionStatus */
|
|
12
|
+
function mapTapStatus(status) {
|
|
13
|
+
switch (status) {
|
|
14
|
+
case 'INITIATED':
|
|
15
|
+
return 'pending';
|
|
16
|
+
case 'IN_PROGRESS':
|
|
17
|
+
return 'processing';
|
|
18
|
+
case 'CAPTURED':
|
|
19
|
+
return 'captured';
|
|
20
|
+
case 'VOID':
|
|
21
|
+
case 'CANCELLED':
|
|
22
|
+
case 'TIMEDOUT':
|
|
23
|
+
case 'ABANDONED':
|
|
24
|
+
return 'cancelled';
|
|
25
|
+
case 'FAILED':
|
|
26
|
+
case 'DECLINED':
|
|
27
|
+
case 'RESTRICTED':
|
|
28
|
+
return 'failed';
|
|
29
|
+
case 'REFUNDED':
|
|
30
|
+
return 'refunded';
|
|
31
|
+
default:
|
|
32
|
+
return 'pending';
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
/** Build a PaymentSession from a Tap charge */
|
|
36
|
+
function chargeToSession(charge, providerId) {
|
|
37
|
+
return {
|
|
38
|
+
id: charge.id,
|
|
39
|
+
providerId,
|
|
40
|
+
status: mapTapStatus(charge.status),
|
|
41
|
+
amount: charge.amount,
|
|
42
|
+
currency: charge.currency,
|
|
43
|
+
providerData: {
|
|
44
|
+
tapChargeId: charge.id,
|
|
45
|
+
tapStatus: charge.status,
|
|
46
|
+
source: charge.source,
|
|
47
|
+
reference: charge.reference,
|
|
48
|
+
},
|
|
49
|
+
redirectUrl: charge.transaction?.url ?? null,
|
|
50
|
+
createdAt: charge.created ?? new Date().toISOString(),
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Tap Payments provider — redirect-based, PCI-free.
|
|
55
|
+
*
|
|
56
|
+
* @example
|
|
57
|
+
* ```ts
|
|
58
|
+
* const tap = new TapPaymentProvider({ secretKey: 'sk_test_...' })
|
|
59
|
+
*
|
|
60
|
+
* // 1. Create charge with token from goSell.js
|
|
61
|
+
* const session = await tap.createSession({
|
|
62
|
+
* amount: 99.99,
|
|
63
|
+
* currency: 'SAR',
|
|
64
|
+
* sourceToken: 'tok_xxx',
|
|
65
|
+
* returnUrl: 'https://store.com/checkout/confirm',
|
|
66
|
+
* })
|
|
67
|
+
*
|
|
68
|
+
* // 2. Redirect customer to session.redirectUrl for 3DS
|
|
69
|
+
*
|
|
70
|
+
* // 3. After redirect, confirm the payment
|
|
71
|
+
* const confirmed = await tap.confirmSession(session.id)
|
|
72
|
+
* // confirmed.status === 'captured'
|
|
73
|
+
* ```
|
|
74
|
+
*/
|
|
75
|
+
export class TapPaymentProvider {
|
|
76
|
+
id = 'tap';
|
|
77
|
+
name = 'Tap Payments';
|
|
78
|
+
secretKey;
|
|
79
|
+
baseUrl;
|
|
80
|
+
webhookSecret;
|
|
81
|
+
constructor(config) {
|
|
82
|
+
this.secretKey = config.secretKey;
|
|
83
|
+
this.baseUrl = config.baseUrl ?? TAP_API_BASE;
|
|
84
|
+
this.webhookSecret = config.webhookSecret ?? null;
|
|
85
|
+
}
|
|
86
|
+
// ---------------------------------------------------------------------------
|
|
87
|
+
// Core methods (required by PaymentProvider)
|
|
88
|
+
// ---------------------------------------------------------------------------
|
|
89
|
+
async createSession(input) {
|
|
90
|
+
const body = {
|
|
91
|
+
amount: input.amount,
|
|
92
|
+
currency: input.currency,
|
|
93
|
+
threeDSecure: true,
|
|
94
|
+
source: { id: input.sourceToken ?? 'src_all' },
|
|
95
|
+
redirect: { url: input.returnUrl ?? '' },
|
|
96
|
+
...(input.orderId ? { reference: { order: input.orderId } } : {}),
|
|
97
|
+
...(input.customerId ? { customer: { id: input.customerId } } : {}),
|
|
98
|
+
// Include customer details if provided (and no customerId)
|
|
99
|
+
...(!input.customerId && input.customer
|
|
100
|
+
? {
|
|
101
|
+
customer: {
|
|
102
|
+
first_name: input.customer.firstName ?? '',
|
|
103
|
+
last_name: input.customer.lastName ?? '',
|
|
104
|
+
email: input.customer.email ?? '',
|
|
105
|
+
phone: input.customer.phone
|
|
106
|
+
? { country_code: '966', number: input.customer.phone.replace(/^\+?966\s?/, '') }
|
|
107
|
+
: undefined,
|
|
108
|
+
},
|
|
109
|
+
}
|
|
110
|
+
: {}),
|
|
111
|
+
...(input.metadata ? { metadata: input.metadata } : {}),
|
|
112
|
+
// Tap sends charge results to this URL asynchronously
|
|
113
|
+
...(input.webhookUrl ? { post: { url: input.webhookUrl } } : {}),
|
|
114
|
+
};
|
|
115
|
+
const charge = await this.request('POST', '/charges', body);
|
|
116
|
+
return chargeToSession(charge, this.id);
|
|
117
|
+
}
|
|
118
|
+
async confirmSession(sessionId) {
|
|
119
|
+
// After 3DS redirect, re-fetch the charge to get the final status
|
|
120
|
+
return this.getSession(sessionId);
|
|
121
|
+
}
|
|
122
|
+
async getSession(sessionId) {
|
|
123
|
+
const charge = await this.request('GET', `/charges/${sessionId}`);
|
|
124
|
+
return chargeToSession(charge, this.id);
|
|
125
|
+
}
|
|
126
|
+
async refund(input) {
|
|
127
|
+
// First get the current charge to know the amount/currency
|
|
128
|
+
const current = await this.getSession(input.sessionId);
|
|
129
|
+
await this.request('POST', '/refunds', {
|
|
130
|
+
charge_id: input.sessionId,
|
|
131
|
+
amount: input.amount ?? current.amount,
|
|
132
|
+
currency: current.currency,
|
|
133
|
+
reason: input.reason ?? 'requested_by_customer',
|
|
134
|
+
});
|
|
135
|
+
// Re-fetch to get updated status
|
|
136
|
+
return this.getSession(input.sessionId);
|
|
137
|
+
}
|
|
138
|
+
// ---------------------------------------------------------------------------
|
|
139
|
+
// Optional methods
|
|
140
|
+
// ---------------------------------------------------------------------------
|
|
141
|
+
async cancelSession(sessionId) {
|
|
142
|
+
// Tap doesn't have a dedicated cancel endpoint.
|
|
143
|
+
// For authorized (not captured) transactions, we could void.
|
|
144
|
+
// For now, re-fetch and return current state.
|
|
145
|
+
return this.getSession(sessionId);
|
|
146
|
+
}
|
|
147
|
+
async verifyWebhook(payload, signature) {
|
|
148
|
+
// Tap's webhook verification uses a "hashstring" — NOT raw body HMAC.
|
|
149
|
+
// The hashstring is built by concatenating specific fields from the charge
|
|
150
|
+
// body with x_ prefixes, then HMAC-SHA256'd with the secret API key.
|
|
151
|
+
//
|
|
152
|
+
// Format for charges/authorizes:
|
|
153
|
+
// x_id{id}x_amount{amount}x_currency{currency}x_gateway_reference{ref}
|
|
154
|
+
// x_payment_reference{ref}x_status{status}x_created{created}
|
|
155
|
+
//
|
|
156
|
+
// See: https://developers.tap.company/docs/webhook#validate-the-webhook-hashstring
|
|
157
|
+
const bodyStr = typeof payload === 'string' ? payload : new TextDecoder().decode(payload);
|
|
158
|
+
const event = JSON.parse(bodyStr);
|
|
159
|
+
// Extract fields for hashstring
|
|
160
|
+
const id = event.id ?? '';
|
|
161
|
+
const amount = event.amount ?? '';
|
|
162
|
+
const currency = event.currency ?? '';
|
|
163
|
+
const gatewayRef = event.reference?.gateway ?? '';
|
|
164
|
+
const paymentRef = event.reference?.payment ?? '';
|
|
165
|
+
const status = event.status ?? '';
|
|
166
|
+
const created = event.transaction?.created ?? '';
|
|
167
|
+
// Build the hashstring input
|
|
168
|
+
const toBeHashed = `x_id${id}` +
|
|
169
|
+
`x_amount${amount}` +
|
|
170
|
+
`x_currency${currency}` +
|
|
171
|
+
`x_gateway_reference${gatewayRef}` +
|
|
172
|
+
`x_payment_reference${paymentRef}` +
|
|
173
|
+
`x_status${status}` +
|
|
174
|
+
`x_created${created}`;
|
|
175
|
+
// HMAC-SHA256 with the secret API key
|
|
176
|
+
const encoder = new TextEncoder();
|
|
177
|
+
const key = await globalThis.crypto.subtle.importKey('raw', encoder.encode(this.secretKey), { name: 'HMAC', hash: 'SHA-256' }, false, ['sign']);
|
|
178
|
+
const sig = await globalThis.crypto.subtle.sign('HMAC', key, encoder.encode(toBeHashed));
|
|
179
|
+
const computed = Array.from(new Uint8Array(sig))
|
|
180
|
+
.map(b => b.toString(16).padStart(2, '0'))
|
|
181
|
+
.join('');
|
|
182
|
+
if (computed !== signature) {
|
|
183
|
+
throw new Error('TapPaymentProvider: invalid webhook hashstring');
|
|
184
|
+
}
|
|
185
|
+
// Map Tap status to a normalized event type
|
|
186
|
+
const eventType = this.mapChargeStatusToEventType(event.status);
|
|
187
|
+
return {
|
|
188
|
+
type: eventType,
|
|
189
|
+
sessionId: String(event.id ?? ''),
|
|
190
|
+
data: event,
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
/** Map Tap charge status to a webhook event type */
|
|
194
|
+
mapChargeStatusToEventType(status) {
|
|
195
|
+
switch (status) {
|
|
196
|
+
case 'CAPTURED': return 'payment.captured';
|
|
197
|
+
case 'FAILED':
|
|
198
|
+
case 'DECLINED':
|
|
199
|
+
case 'RESTRICTED': return 'payment.failed';
|
|
200
|
+
case 'VOID':
|
|
201
|
+
case 'CANCELLED': return 'payment.cancelled';
|
|
202
|
+
case 'REFUNDED': return 'payment.refunded';
|
|
203
|
+
case 'TIMEDOUT':
|
|
204
|
+
case 'ABANDONED': return 'payment.expired';
|
|
205
|
+
default: return 'payment.updated';
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
// ---------------------------------------------------------------------------
|
|
209
|
+
// HTTP helper
|
|
210
|
+
// ---------------------------------------------------------------------------
|
|
211
|
+
async request(method, path, body) {
|
|
212
|
+
const url = `${this.baseUrl}${path}`;
|
|
213
|
+
const headers = {
|
|
214
|
+
'Authorization': `Bearer ${this.secretKey}`,
|
|
215
|
+
'Content-Type': 'application/json',
|
|
216
|
+
'Accept': 'application/json',
|
|
217
|
+
};
|
|
218
|
+
const res = await fetch(url, {
|
|
219
|
+
method,
|
|
220
|
+
headers,
|
|
221
|
+
...(body ? { body: JSON.stringify(body) } : {}),
|
|
222
|
+
});
|
|
223
|
+
if (!res.ok) {
|
|
224
|
+
const errorBody = await res.text();
|
|
225
|
+
throw new Error(`Tap API error (${res.status}): ${errorBody}`);
|
|
226
|
+
}
|
|
227
|
+
return res.json();
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
//# sourceMappingURL=tap-provider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tap-provider.js","sourceRoot":"","sources":["../src/tap-provider.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/** Configuration for TapPaymentProvider */
|
|
2
|
+
export interface TapConfig {
|
|
3
|
+
/** Tap secret API key (sk_test_... or sk_live_...) */
|
|
4
|
+
secretKey: string;
|
|
5
|
+
/** Tap webhook secret for signature verification (optional) */
|
|
6
|
+
webhookSecret?: string;
|
|
7
|
+
/** Use live mode (defaults to auto-detect from key prefix) */
|
|
8
|
+
liveMode?: boolean;
|
|
9
|
+
/** API base URL override (for testing) */
|
|
10
|
+
baseUrl?: string;
|
|
11
|
+
}
|
|
12
|
+
/** Tap charge object (subset of fields we use) */
|
|
13
|
+
export interface TapRawCharge {
|
|
14
|
+
id: string;
|
|
15
|
+
status: TapChargeStatus;
|
|
16
|
+
amount: number;
|
|
17
|
+
currency: string;
|
|
18
|
+
threeDSecure: boolean;
|
|
19
|
+
description?: string;
|
|
20
|
+
reference?: {
|
|
21
|
+
transaction?: string;
|
|
22
|
+
order?: string;
|
|
23
|
+
};
|
|
24
|
+
transaction?: {
|
|
25
|
+
url?: string;
|
|
26
|
+
created?: string;
|
|
27
|
+
authorization_id?: string;
|
|
28
|
+
};
|
|
29
|
+
redirect?: {
|
|
30
|
+
status?: string;
|
|
31
|
+
url?: string;
|
|
32
|
+
};
|
|
33
|
+
source?: {
|
|
34
|
+
id: string;
|
|
35
|
+
type: string;
|
|
36
|
+
payment_method?: string;
|
|
37
|
+
payment_type?: string;
|
|
38
|
+
};
|
|
39
|
+
customer?: {
|
|
40
|
+
id?: string;
|
|
41
|
+
first_name?: string;
|
|
42
|
+
last_name?: string;
|
|
43
|
+
email?: string;
|
|
44
|
+
};
|
|
45
|
+
metadata?: Record<string, unknown>;
|
|
46
|
+
created: string;
|
|
47
|
+
}
|
|
48
|
+
/** Known Tap charge status values */
|
|
49
|
+
export type TapChargeStatus = 'INITIATED' | 'IN_PROGRESS' | 'ABANDONED' | 'CAPTURED' | 'VOID' | 'CANCELLED' | 'FAILED' | 'DECLINED' | 'RESTRICTED' | 'REFUNDED' | 'TIMEDOUT';
|
|
50
|
+
/** Tap refund response */
|
|
51
|
+
export interface TapRawRefund {
|
|
52
|
+
id: string;
|
|
53
|
+
status: string;
|
|
54
|
+
amount: number;
|
|
55
|
+
currency: string;
|
|
56
|
+
charge_id: string;
|
|
57
|
+
reason?: string;
|
|
58
|
+
created: string;
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAIA,2CAA2C;AAC3C,MAAM,WAAW,SAAS;IACxB,sDAAsD;IACtD,SAAS,EAAE,MAAM,CAAA;IACjB,+DAA+D;IAC/D,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,8DAA8D;IAC9D,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,0CAA0C;IAC1C,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB;AAID,kDAAkD;AAClD,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAA;IACV,MAAM,EAAE,eAAe,CAAA;IACvB,MAAM,EAAE,MAAM,CAAA;IACd,QAAQ,EAAE,MAAM,CAAA;IAChB,YAAY,EAAE,OAAO,CAAA;IACrB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,SAAS,CAAC,EAAE;QACV,WAAW,CAAC,EAAE,MAAM,CAAA;QACpB,KAAK,CAAC,EAAE,MAAM,CAAA;KACf,CAAA;IACD,WAAW,CAAC,EAAE;QACZ,GAAG,CAAC,EAAE,MAAM,CAAA;QACZ,OAAO,CAAC,EAAE,MAAM,CAAA;QAChB,gBAAgB,CAAC,EAAE,MAAM,CAAA;KAC1B,CAAA;IACD,QAAQ,CAAC,EAAE;QACT,MAAM,CAAC,EAAE,MAAM,CAAA;QACf,GAAG,CAAC,EAAE,MAAM,CAAA;KACb,CAAA;IACD,MAAM,CAAC,EAAE;QACP,EAAE,EAAE,MAAM,CAAA;QACV,IAAI,EAAE,MAAM,CAAA;QACZ,cAAc,CAAC,EAAE,MAAM,CAAA;QACvB,YAAY,CAAC,EAAE,MAAM,CAAA;KACtB,CAAA;IACD,QAAQ,CAAC,EAAE;QACT,EAAE,CAAC,EAAE,MAAM,CAAA;QACX,UAAU,CAAC,EAAE,MAAM,CAAA;QACnB,SAAS,CAAC,EAAE,MAAM,CAAA;QAClB,KAAK,CAAC,EAAE,MAAM,CAAA;KACf,CAAA;IACD,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAClC,OAAO,EAAE,MAAM,CAAA;CAChB;AAED,qCAAqC;AACrC,MAAM,MAAM,eAAe,GACvB,WAAW,GACX,aAAa,GACb,WAAW,GACX,UAAU,GACV,MAAM,GACN,WAAW,GACX,QAAQ,GACR,UAAU,GACV,YAAY,GACZ,UAAU,GACV,UAAU,CAAA;AAEd,0BAA0B;AAC1B,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAA;IACV,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,MAAM,CAAA;IACd,QAAQ,EAAE,MAAM,CAAA;IAChB,SAAS,EAAE,MAAM,CAAA;IACjB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,OAAO,EAAE,MAAM,CAAA;CAChB"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,mDAAmD;AACnD,8EAA8E"}
|
package/package.json
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@commercejs/payment-tap",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Tap Payments provider for CommerceJS — redirect-based, PCI-free",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"main": "./dist/index.js",
|
|
8
|
+
"module": "./dist/index.js",
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"exports": {
|
|
11
|
+
".": {
|
|
12
|
+
"types": "./dist/index.d.ts",
|
|
13
|
+
"import": "./dist/index.js"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"dist"
|
|
18
|
+
],
|
|
19
|
+
"dependencies": {
|
|
20
|
+
"@commercejs/types": "0.1.0"
|
|
21
|
+
},
|
|
22
|
+
"devDependencies": {
|
|
23
|
+
"typescript": "^5.7.0",
|
|
24
|
+
"vitest": "^4.0.0"
|
|
25
|
+
},
|
|
26
|
+
"scripts": {
|
|
27
|
+
"build": "tsc",
|
|
28
|
+
"typecheck": "tsc --noEmit",
|
|
29
|
+
"test": "vitest run",
|
|
30
|
+
"clean": "rm -rf dist"
|
|
31
|
+
}
|
|
32
|
+
}
|