tupay 0.5.2

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.
Files changed (116) hide show
  1. package/README.md +358 -0
  2. package/dist/cjs/client.d.ts +48 -0
  3. package/dist/cjs/client.d.ts.map +1 -0
  4. package/dist/cjs/client.js +165 -0
  5. package/dist/cjs/client.js.map +1 -0
  6. package/dist/cjs/errors.d.ts +66 -0
  7. package/dist/cjs/errors.d.ts.map +1 -0
  8. package/dist/cjs/errors.js +105 -0
  9. package/dist/cjs/errors.js.map +1 -0
  10. package/dist/cjs/index.d.ts +86 -0
  11. package/dist/cjs/index.d.ts.map +1 -0
  12. package/dist/cjs/index.js +110 -0
  13. package/dist/cjs/index.js.map +1 -0
  14. package/dist/cjs/liste.d.ts +54 -0
  15. package/dist/cjs/liste.d.ts.map +1 -0
  16. package/dist/cjs/liste.js +36 -0
  17. package/dist/cjs/liste.js.map +1 -0
  18. package/dist/cjs/package.json +3 -0
  19. package/dist/cjs/resources/events.d.ts +64 -0
  20. package/dist/cjs/resources/events.d.ts.map +1 -0
  21. package/dist/cjs/resources/events.js +104 -0
  22. package/dist/cjs/resources/events.js.map +1 -0
  23. package/dist/cjs/resources/merchants.d.ts +19 -0
  24. package/dist/cjs/resources/merchants.d.ts.map +1 -0
  25. package/dist/cjs/resources/merchants.js +29 -0
  26. package/dist/cjs/resources/merchants.js.map +1 -0
  27. package/dist/cjs/resources/payment-intents.d.ts +29 -0
  28. package/dist/cjs/resources/payment-intents.d.ts.map +1 -0
  29. package/dist/cjs/resources/payment-intents.js +22 -0
  30. package/dist/cjs/resources/payment-intents.js.map +1 -0
  31. package/dist/cjs/resources/payment-links.d.ts +38 -0
  32. package/dist/cjs/resources/payment-links.d.ts.map +1 -0
  33. package/dist/cjs/resources/payment-links.js +56 -0
  34. package/dist/cjs/resources/payment-links.js.map +1 -0
  35. package/dist/cjs/resources/refunds.d.ts +27 -0
  36. package/dist/cjs/resources/refunds.d.ts.map +1 -0
  37. package/dist/cjs/resources/refunds.js +33 -0
  38. package/dist/cjs/resources/refunds.js.map +1 -0
  39. package/dist/cjs/resources/test-helpers.d.ts +42 -0
  40. package/dist/cjs/resources/test-helpers.d.ts.map +1 -0
  41. package/dist/cjs/resources/test-helpers.js +50 -0
  42. package/dist/cjs/resources/test-helpers.js.map +1 -0
  43. package/dist/cjs/resources/transactions.d.ts +29 -0
  44. package/dist/cjs/resources/transactions.d.ts.map +1 -0
  45. package/dist/cjs/resources/transactions.js +65 -0
  46. package/dist/cjs/resources/transactions.js.map +1 -0
  47. package/dist/cjs/resources/webhook-endpoints.d.ts +43 -0
  48. package/dist/cjs/resources/webhook-endpoints.d.ts.map +1 -0
  49. package/dist/cjs/resources/webhook-endpoints.js +71 -0
  50. package/dist/cjs/resources/webhook-endpoints.js.map +1 -0
  51. package/dist/cjs/types.d.ts +166 -0
  52. package/dist/cjs/types.d.ts.map +1 -0
  53. package/dist/cjs/types.js +10 -0
  54. package/dist/cjs/types.js.map +1 -0
  55. package/dist/cjs/webhooks.d.ts +31 -0
  56. package/dist/cjs/webhooks.d.ts.map +1 -0
  57. package/dist/cjs/webhooks.js +81 -0
  58. package/dist/cjs/webhooks.js.map +1 -0
  59. package/dist/esm/client.d.ts +48 -0
  60. package/dist/esm/client.d.ts.map +1 -0
  61. package/dist/esm/client.js +161 -0
  62. package/dist/esm/client.js.map +1 -0
  63. package/dist/esm/errors.d.ts +66 -0
  64. package/dist/esm/errors.d.ts.map +1 -0
  65. package/dist/esm/errors.js +90 -0
  66. package/dist/esm/errors.js.map +1 -0
  67. package/dist/esm/index.d.ts +86 -0
  68. package/dist/esm/index.d.ts.map +1 -0
  69. package/dist/esm/index.js +90 -0
  70. package/dist/esm/index.js.map +1 -0
  71. package/dist/esm/liste.d.ts +54 -0
  72. package/dist/esm/liste.d.ts.map +1 -0
  73. package/dist/esm/liste.js +32 -0
  74. package/dist/esm/liste.js.map +1 -0
  75. package/dist/esm/package.json +3 -0
  76. package/dist/esm/resources/events.d.ts +64 -0
  77. package/dist/esm/resources/events.d.ts.map +1 -0
  78. package/dist/esm/resources/events.js +100 -0
  79. package/dist/esm/resources/events.js.map +1 -0
  80. package/dist/esm/resources/merchants.d.ts +19 -0
  81. package/dist/esm/resources/merchants.d.ts.map +1 -0
  82. package/dist/esm/resources/merchants.js +25 -0
  83. package/dist/esm/resources/merchants.js.map +1 -0
  84. package/dist/esm/resources/payment-intents.d.ts +29 -0
  85. package/dist/esm/resources/payment-intents.d.ts.map +1 -0
  86. package/dist/esm/resources/payment-intents.js +18 -0
  87. package/dist/esm/resources/payment-intents.js.map +1 -0
  88. package/dist/esm/resources/payment-links.d.ts +38 -0
  89. package/dist/esm/resources/payment-links.d.ts.map +1 -0
  90. package/dist/esm/resources/payment-links.js +52 -0
  91. package/dist/esm/resources/payment-links.js.map +1 -0
  92. package/dist/esm/resources/refunds.d.ts +27 -0
  93. package/dist/esm/resources/refunds.d.ts.map +1 -0
  94. package/dist/esm/resources/refunds.js +29 -0
  95. package/dist/esm/resources/refunds.js.map +1 -0
  96. package/dist/esm/resources/test-helpers.d.ts +42 -0
  97. package/dist/esm/resources/test-helpers.d.ts.map +1 -0
  98. package/dist/esm/resources/test-helpers.js +46 -0
  99. package/dist/esm/resources/test-helpers.js.map +1 -0
  100. package/dist/esm/resources/transactions.d.ts +29 -0
  101. package/dist/esm/resources/transactions.d.ts.map +1 -0
  102. package/dist/esm/resources/transactions.js +61 -0
  103. package/dist/esm/resources/transactions.js.map +1 -0
  104. package/dist/esm/resources/webhook-endpoints.d.ts +43 -0
  105. package/dist/esm/resources/webhook-endpoints.d.ts.map +1 -0
  106. package/dist/esm/resources/webhook-endpoints.js +67 -0
  107. package/dist/esm/resources/webhook-endpoints.js.map +1 -0
  108. package/dist/esm/types.d.ts +166 -0
  109. package/dist/esm/types.d.ts.map +1 -0
  110. package/dist/esm/types.js +9 -0
  111. package/dist/esm/types.js.map +1 -0
  112. package/dist/esm/webhooks.d.ts +31 -0
  113. package/dist/esm/webhooks.d.ts.map +1 -0
  114. package/dist/esm/webhooks.js +77 -0
  115. package/dist/esm/webhooks.js.map +1 -0
  116. package/package.json +53 -0
@@ -0,0 +1,166 @@
1
+ /**
2
+ * Objets exposés par l'API Tupay.
3
+ *
4
+ * Règle de lecture des montants : `amountXpf` est un ENTIER de francs
5
+ * pacifiques. Le XPF n'a pas de centimes — ne jamais diviser par 100.
6
+ * `amountEurCents` n'existe que pour la réconciliation bancaire.
7
+ */
8
+ export type TransactionStatus = 'pending' | 'processing' | 'succeeded' | 'failed' | 'refunded';
9
+ export interface Transaction {
10
+ id: string;
11
+ object: 'transaction';
12
+ amountXpf: number;
13
+ amountEurCents: number;
14
+ currency: 'XPF';
15
+ status: TransactionStatus;
16
+ paymentIntentId: string;
17
+ paymentLinkId: string | null;
18
+ livemode: boolean;
19
+ /**
20
+ * Cumul remboursé. **C'est ce champ qui fait foi**, pas `status` :
21
+ * un remboursement partiel laisse `status` à `succeeded`.
22
+ */
23
+ refundedAmountXpf: number;
24
+ refundedAt: string | null;
25
+ createdAt: string;
26
+ updatedAt: string;
27
+ }
28
+ export interface PaymentLink {
29
+ id: string;
30
+ object: 'payment_link';
31
+ amountXpf: number;
32
+ currency: 'XPF';
33
+ description: string;
34
+ slug: string;
35
+ /** URL publique à transmettre au client. */
36
+ url: string;
37
+ returnUrl: string | null;
38
+ metadata: Record<string, string>;
39
+ active: boolean;
40
+ livemode: boolean;
41
+ createdAt: string;
42
+ updatedAt: string;
43
+ }
44
+ export interface PaymentIntent {
45
+ intentId: string;
46
+ /** Identifiant Tupay — à conserver : remboursements, webhooks, export. */
47
+ transactionId: string;
48
+ clientSecret: string | null;
49
+ amountXpf: number;
50
+ amountEurCents: number;
51
+ livemode: boolean;
52
+ }
53
+ export type RefundStatus = 'pending' | 'succeeded' | 'failed';
54
+ export interface Refund {
55
+ id: string;
56
+ object: 'refund';
57
+ transactionId: string;
58
+ amountXpf: number;
59
+ currency: 'XPF';
60
+ status: RefundStatus;
61
+ reason: string | null;
62
+ livemode: boolean;
63
+ createdAt: string;
64
+ }
65
+ export type WebhookEventType = 'payment.succeeded' | 'payment.failed' | 'payment.processing' | 'payment.refunded';
66
+ export interface WebhookEndpoint {
67
+ id: string;
68
+ object: 'webhook_endpoint';
69
+ url: string;
70
+ description: string | null;
71
+ enabledEvents: (WebhookEventType | '*')[];
72
+ status: 'enabled' | 'disabled';
73
+ livemode: boolean;
74
+ createdAt: string;
75
+ updatedAt: string;
76
+ disabledAt: string | null;
77
+ }
78
+ /** Seule la création renvoie `secret` — il n'est plus jamais lisible ensuite. */
79
+ export interface WebhookEndpointWithSecret extends WebhookEndpoint {
80
+ secret: string;
81
+ }
82
+ export interface WebhookDelivery {
83
+ id: string;
84
+ object: 'webhook_delivery';
85
+ eventId: string;
86
+ eventType: string;
87
+ status: 'pending' | 'succeeded' | 'failed' | 'exhausted';
88
+ attempts: number;
89
+ responseStatus: number | null;
90
+ responseBody: string | null;
91
+ lastError: string | null;
92
+ nextRetryAt: string | null;
93
+ deliveredAt: string | null;
94
+ createdAt: string;
95
+ }
96
+ export interface Merchant {
97
+ id: string;
98
+ object: 'merchant';
99
+ email: string;
100
+ name: string;
101
+ /** Doit valoir `validated` pour encaisser en mode live. */
102
+ kyb_status: 'pending' | 'validated' | 'rejected';
103
+ iban_xpf: string | null;
104
+ tgc_rate: 'exempt' | '5' | '10' | '16';
105
+ livemode: boolean;
106
+ created_at: string;
107
+ updated_at: string;
108
+ }
109
+ /** Enveloppe reçue sur un endpoint de webhook. */
110
+ export interface WebhookEvent {
111
+ /** Dédupliquez sur cet identifiant : une livraison peut se répéter. */
112
+ id: string;
113
+ object: 'event';
114
+ type: WebhookEventType;
115
+ apiVersion: string;
116
+ /** Horodatage Unix en secondes. */
117
+ created: number;
118
+ livemode: boolean;
119
+ data: {
120
+ object: Transaction;
121
+ };
122
+ }
123
+ /**
124
+ * Événement tel que l'API le RESTITUE (`tupay.events.list/retrieve`).
125
+ *
126
+ * ⚠️ À ne pas confondre avec `WebhookEvent`, l'enveloppe REÇUE sur votre
127
+ * endpoint. Les deux décrivent le même fait, mais l'enveloppe porte en
128
+ * plus `apiVersion` — elle est construite au moment de la livraison,
129
+ * pour la version de l'endpoint destinataire. Un même événement peut
130
+ * donc être livré sous deux formes à deux endpoints.
131
+ */
132
+ export interface TupayEvent {
133
+ id: string;
134
+ object: 'event';
135
+ type: WebhookEventType;
136
+ /** Horodatage Unix en secondes. */
137
+ created: number;
138
+ livemode: boolean;
139
+ data: {
140
+ object: Transaction;
141
+ };
142
+ }
143
+ /** Retour de `tupay.events.resend`. */
144
+ export interface ResendResult {
145
+ object: 'event_resend';
146
+ eventId: string;
147
+ /** Nombre de livraisons créées — une par endpoint actif. */
148
+ deliveries: number;
149
+ }
150
+ /**
151
+ * Une page de collection.
152
+ *
153
+ * Les deux clés portent la MÊME valeur, quelle que soit celle rendue par
154
+ * l'API : `normaliserListe` (`src/liste.ts`) lit `has_more` avec repli
155
+ * sur `hasMore` et remplit les deux. Un SDK neuf parle donc à une API
156
+ * ancienne, et un SDK ancien à une API neuve.
157
+ */
158
+ export interface ListResult<T> {
159
+ data: T[];
160
+ /** Vrai s'il reste au moins un élément dans le sens du parcours. */
161
+ has_more: boolean;
162
+ /** @deprecated Doublon de `has_more`, de même valeur. Lisez `has_more`. */
163
+ hasMore: boolean;
164
+ }
165
+ export type { ListParams } from './liste.js';
166
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ /**
3
+ * Objets exposés par l'API Tupay.
4
+ *
5
+ * Règle de lecture des montants : `amountXpf` est un ENTIER de francs
6
+ * pacifiques. Le XPF n'a pas de centimes — ne jamais diviser par 100.
7
+ * `amountEurCents` n'existe que pour la réconciliation bancaire.
8
+ */
9
+ Object.defineProperty(exports, "__esModule", { value: true });
10
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":";AAAA;;;;;;GAMG"}
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Vérification des webhooks entrants (Tupay → votre serveur).
3
+ *
4
+ * ⚠️ Le point qui casse le plus souvent : il faut passer le corps **brut**
5
+ * de la requête, pas un objet déjà parsé puis re-sérialisé. Le HMAC porte
6
+ * sur les octets exacts qu'on a envoyés ; un espace d'écart le casse.
7
+ *
8
+ * Express → app.post('/hook', express.raw({ type: 'application/json' }), …)
9
+ * Next.js → const raw = await request.text()
10
+ * Fastify → config: { rawBody: true }
11
+ */
12
+ import type { WebhookEvent } from './types.js';
13
+ /** Écart maximum toléré entre l'horodatage signé et l'heure locale. */
14
+ export declare const DEFAULT_TOLERANCE_SECONDS = 300;
15
+ export interface ConstructEventOptions {
16
+ /** Défaut 300 s. Mettre 0 désactive le contrôle (déconseillé). */
17
+ toleranceSeconds?: number;
18
+ /** Injectable pour les tests. */
19
+ nowSeconds?: number;
20
+ }
21
+ /**
22
+ * Vérifie la signature et renvoie l'événement typé.
23
+ *
24
+ * @param payload corps BRUT de la requête (string ou Buffer)
25
+ * @param header valeur de l'en-tête `Tupay-Signature`
26
+ * @param secret secret de l'endpoint (`whsec_…`)
27
+ * @throws {TupaySignatureVerificationError} si la signature ou l'horodatage
28
+ * est invalide — répondez alors 400 sans traiter l'événement.
29
+ */
30
+ export declare function constructEvent(payload: string | Buffer, header: string | null | undefined, secret: string, options?: ConstructEventOptions): WebhookEvent;
31
+ //# sourceMappingURL=webhooks.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"webhooks.d.ts","sourceRoot":"","sources":["../../src/webhooks.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAIH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAE9C,uEAAuE;AACvE,eAAO,MAAM,yBAAyB,MAAM,CAAA;AAE5C,MAAM,WAAW,qBAAqB;IACpC,kEAAkE;IAClE,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,iCAAiC;IACjC,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB;AA2BD;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAC5B,OAAO,EAAE,MAAM,GAAG,MAAM,EACxB,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EACjC,MAAM,EAAE,MAAM,EACd,OAAO,GAAE,qBAA0B,GAClC,YAAY,CAiDd"}
@@ -0,0 +1,81 @@
1
+ "use strict";
2
+ /**
3
+ * Vérification des webhooks entrants (Tupay → votre serveur).
4
+ *
5
+ * ⚠️ Le point qui casse le plus souvent : il faut passer le corps **brut**
6
+ * de la requête, pas un objet déjà parsé puis re-sérialisé. Le HMAC porte
7
+ * sur les octets exacts qu'on a envoyés ; un espace d'écart le casse.
8
+ *
9
+ * Express → app.post('/hook', express.raw({ type: 'application/json' }), …)
10
+ * Next.js → const raw = await request.text()
11
+ * Fastify → config: { rawBody: true }
12
+ */
13
+ Object.defineProperty(exports, "__esModule", { value: true });
14
+ exports.DEFAULT_TOLERANCE_SECONDS = void 0;
15
+ exports.constructEvent = constructEvent;
16
+ const node_crypto_1 = require("node:crypto");
17
+ const errors_js_1 = require("./errors.js");
18
+ /** Écart maximum toléré entre l'horodatage signé et l'heure locale. */
19
+ exports.DEFAULT_TOLERANCE_SECONDS = 300;
20
+ function parseHeader(header) {
21
+ let timestamp = null;
22
+ let v1 = null;
23
+ for (const part of header.split(',')) {
24
+ const separator = part.indexOf('=');
25
+ if (separator === -1)
26
+ continue;
27
+ const key = part.slice(0, separator).trim();
28
+ const value = part.slice(separator + 1).trim();
29
+ if (key === 't') {
30
+ const parsed = Number.parseInt(value, 10);
31
+ if (Number.isFinite(parsed))
32
+ timestamp = parsed;
33
+ }
34
+ else if (key === 'v1') {
35
+ v1 = value;
36
+ }
37
+ }
38
+ return timestamp === null || v1 === null ? null : { timestamp, v1 };
39
+ }
40
+ /**
41
+ * Vérifie la signature et renvoie l'événement typé.
42
+ *
43
+ * @param payload corps BRUT de la requête (string ou Buffer)
44
+ * @param header valeur de l'en-tête `Tupay-Signature`
45
+ * @param secret secret de l'endpoint (`whsec_…`)
46
+ * @throws {TupaySignatureVerificationError} si la signature ou l'horodatage
47
+ * est invalide — répondez alors 400 sans traiter l'événement.
48
+ */
49
+ function constructEvent(payload, header, secret, options = {}) {
50
+ if (!secret) {
51
+ throw new errors_js_1.TupaySignatureVerificationError('Secret de webhook manquant. Récupérez-le à la création de l’endpoint (whsec_…).');
52
+ }
53
+ if (!header) {
54
+ throw new errors_js_1.TupaySignatureVerificationError('En-tête Tupay-Signature absent de la requête.');
55
+ }
56
+ const parts = parseHeader(header);
57
+ if (!parts) {
58
+ throw new errors_js_1.TupaySignatureVerificationError('En-tête Tupay-Signature malformé — format attendu : t=<unix>,v1=<hex>.');
59
+ }
60
+ const tolerance = options.toleranceSeconds ?? exports.DEFAULT_TOLERANCE_SECONDS;
61
+ const now = options.nowSeconds ?? Math.floor(Date.now() / 1000);
62
+ if (tolerance > 0 && Math.abs(now - parts.timestamp) > tolerance) {
63
+ throw new errors_js_1.TupaySignatureVerificationError(`Horodatage hors tolérance (${tolerance} s) — possible rejeu d’une livraison capturée.`);
64
+ }
65
+ const raw = typeof payload === 'string' ? payload : payload.toString('utf8');
66
+ const expected = (0, node_crypto_1.createHmac)('sha256', secret)
67
+ .update(`${parts.timestamp}.${raw}`, 'utf8')
68
+ .digest('hex');
69
+ // Longueurs comparées d'abord : timingSafeEqual lève si elles diffèrent.
70
+ if (expected.length !== parts.v1.length ||
71
+ !(0, node_crypto_1.timingSafeEqual)(Buffer.from(expected, 'utf8'), Buffer.from(parts.v1, 'utf8'))) {
72
+ throw new errors_js_1.TupaySignatureVerificationError('Signature invalide — la charge utile ne correspond pas au secret de cet endpoint.');
73
+ }
74
+ try {
75
+ return JSON.parse(raw);
76
+ }
77
+ catch {
78
+ throw new errors_js_1.TupaySignatureVerificationError('Signature valide mais corps illisible : le JSON est malformé.');
79
+ }
80
+ }
81
+ //# sourceMappingURL=webhooks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"webhooks.js","sourceRoot":"","sources":["../../src/webhooks.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Cœur HTTP du SDK : authentification, idempotence, reprises, erreurs.
3
+ *
4
+ * Aucune dépendance runtime — `fetch` global (Node 18+) et `node:crypto`.
5
+ */
6
+ export interface TupayOptions {
7
+ /** Défaut : l’hôte de production Tupay. Surchargez pour pointer un environnement local. */
8
+ baseUrl?: string;
9
+ /** Timeout par tentative, en millisecondes. Défaut 20 000. */
10
+ timeout?: number;
11
+ /** Nombre de reprises après la tentative initiale. Défaut 2. */
12
+ maxRetries?: number;
13
+ /** En-têtes ajoutés à chaque requête (traçage, proxy d'entreprise…). */
14
+ headers?: Record<string, string>;
15
+ /**
16
+ * Version d'API à demander, au format `AAAA-MM-JJ`.
17
+ *
18
+ * Facultative : sans elle, la version figée à la création de la clé
19
+ * s'applique, et les formes de réponse restent stables dans le temps.
20
+ * La préciser sert à coder explicitement contre une version donnée —
21
+ * une valeur inconnue est refusée par un `400`, jamais silencieusement
22
+ * ignorée.
23
+ */
24
+ apiVersion?: string;
25
+ /** Injectable pour les tests. Défaut : `globalThis.fetch`. */
26
+ fetch?: typeof globalThis.fetch;
27
+ }
28
+ export interface RequestOptions {
29
+ method: 'GET' | 'POST' | 'PATCH' | 'DELETE';
30
+ path: string;
31
+ query?: Record<string, string | number | boolean | undefined>;
32
+ body?: unknown;
33
+ /**
34
+ * Clé d'idempotence. Sa présence rend la requête rejouable en toute
35
+ * sécurité : c'est elle qui autorise le SDK à retenter un POST.
36
+ */
37
+ idempotencyKey?: string;
38
+ }
39
+ export declare class TupayClient {
40
+ #private;
41
+ /** `true` si la clé est une `tpk_live_`. Déduit du préfixe, sans appel réseau. */
42
+ readonly livemode: boolean;
43
+ constructor(apiKey: string, options?: TupayOptions);
44
+ /** Génère une clé d'idempotence. Exposé pour que l'appelant puisse la réutiliser. */
45
+ static idempotencyKey(): string;
46
+ request<T>(options: RequestOptions): Promise<T>;
47
+ }
48
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/client.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAKH,MAAM,WAAW,YAAY;IAC3B,2FAA2F;IAC3F,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,8DAA8D;IAC9D,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,gEAAgE;IAChE,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,wEAAwE;IACxE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAChC;;;;;;;;OAQG;IACH,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,8DAA8D;IAC9D,KAAK,CAAC,EAAE,OAAO,UAAU,CAAC,KAAK,CAAA;CAChC;AAED,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,KAAK,GAAG,MAAM,GAAG,OAAO,GAAG,QAAQ,CAAA;IAC3C,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,SAAS,CAAC,CAAA;IAC7D,IAAI,CAAC,EAAE,OAAO,CAAA;IACd;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,CAAA;CACxB;AAsBD,qBAAa,WAAW;;IAQtB,kFAAkF;IAClF,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAA;gBAEd,MAAM,EAAE,MAAM,EAAE,OAAO,GAAE,YAAiB;IAkCtD,qFAAqF;IACrF,MAAM,CAAC,cAAc,IAAI,MAAM;IAIzB,OAAO,CAAC,CAAC,EAAE,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,CAAC,CAAC;CAmHtD"}
@@ -0,0 +1,161 @@
1
+ /**
2
+ * Cœur HTTP du SDK : authentification, idempotence, reprises, erreurs.
3
+ *
4
+ * Aucune dépendance runtime — `fetch` global (Node 18+) et `node:crypto`.
5
+ */
6
+ import { randomUUID } from 'node:crypto';
7
+ import { errorFromResponse, TupayConnectionError, TupayError } from './errors.js';
8
+ /**
9
+ * Hôte de production.
10
+ *
11
+ * ⚠️ `api.tupay.pf` ne résout pas — le domaine n'est pas enregistré. Un
12
+ * SDK publié avec cette valeur échouerait en erreur DNS au tout premier
13
+ * appel, ce qui est la pire première impression possible. On pointe donc
14
+ * l'hôte qui répond réellement aujourd'hui.
15
+ *
16
+ * Le jour où `api.tupay.pf` existe, changer cette constante est un
17
+ * changement de comportement pour quiconque s'est reposé sur le défaut :
18
+ * il devra donc sortir en version MAJEURE, avec les deux hôtes servis en
19
+ * parallèle le temps de la transition.
20
+ */
21
+ const DEFAULT_BASE_URL = 'https://tupay.apps.lepetittahitien.dev';
22
+ const DEFAULT_TIMEOUT_MS = 20_000;
23
+ const DEFAULT_MAX_RETRIES = 2;
24
+ /** Statuts qui méritent une reprise : surcharge passagère ou incident serveur. */
25
+ const RETRYABLE_STATUSES = new Set([429, 500, 502, 503, 504]);
26
+ export class TupayClient {
27
+ #apiKey;
28
+ #baseUrl;
29
+ #timeout;
30
+ #maxRetries;
31
+ #headers;
32
+ #fetch;
33
+ /** `true` si la clé est une `tpk_live_`. Déduit du préfixe, sans appel réseau. */
34
+ livemode;
35
+ constructor(apiKey, options = {}) {
36
+ if (!apiKey) {
37
+ throw new TupayError('Clé API manquante. Passez-la au constructeur : new Tupay(process.env.TUPAY_SECRET_KEY).', { code: 'missing_api_key' });
38
+ }
39
+ if (!apiKey.startsWith('tpk_live_') && !apiKey.startsWith('tpk_test_')) {
40
+ throw new TupayError('Clé API invalide : elle doit commencer par tpk_live_ ou tpk_test_.', { code: 'invalid_api_key' });
41
+ }
42
+ const fetchImpl = options.fetch ?? globalThis.fetch;
43
+ if (typeof fetchImpl !== 'function') {
44
+ throw new TupayError('fetch global introuvable. Node 18+ est requis, ou passez une implémentation via l’option `fetch`.', { code: 'fetch_unavailable' });
45
+ }
46
+ this.#apiKey = apiKey;
47
+ this.#baseUrl = (options.baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/, '');
48
+ this.#timeout = options.timeout ?? DEFAULT_TIMEOUT_MS;
49
+ this.#maxRetries = options.maxRetries ?? DEFAULT_MAX_RETRIES;
50
+ this.#headers = {
51
+ ...(options.apiVersion ? { 'tupay-version': options.apiVersion } : {}),
52
+ ...(options.headers ?? {}),
53
+ };
54
+ this.#fetch = fetchImpl;
55
+ this.livemode = apiKey.startsWith('tpk_live_');
56
+ }
57
+ /** Génère une clé d'idempotence. Exposé pour que l'appelant puisse la réutiliser. */
58
+ static idempotencyKey() {
59
+ return randomUUID();
60
+ }
61
+ async request(options) {
62
+ const url = this.#buildUrl(options.path, options.query);
63
+ const headers = {
64
+ authorization: `Bearer ${this.#apiKey}`,
65
+ accept: 'application/json',
66
+ 'user-agent': 'tupay-node',
67
+ ...this.#headers,
68
+ };
69
+ if (options.body !== undefined) {
70
+ headers['content-type'] = 'application/json';
71
+ }
72
+ if (options.idempotencyKey) {
73
+ headers['idempotency-key'] = options.idempotencyKey;
74
+ }
75
+ // Une requête n'est rejouable que si elle est sûre à répéter :
76
+ // lecture pure, ou écriture protégée par une clé d'idempotence.
77
+ // Sans ça, une reprise sur timeout créerait un doublon.
78
+ const retryable = options.method === 'GET' || options.idempotencyKey !== undefined;
79
+ const attempts = retryable ? this.#maxRetries + 1 : 1;
80
+ let lastError;
81
+ for (let attempt = 1; attempt <= attempts; attempt++) {
82
+ try {
83
+ const response = await this.#fetch(url, {
84
+ method: options.method,
85
+ headers,
86
+ body: options.body === undefined ? undefined : JSON.stringify(options.body),
87
+ signal: AbortSignal.timeout(this.#timeout),
88
+ });
89
+ const requestId = response.headers.get('x-request-id') ?? undefined;
90
+ if (response.ok) {
91
+ if (response.status === 204)
92
+ return undefined;
93
+ const text = await response.text();
94
+ if (!text)
95
+ return undefined;
96
+ return JSON.parse(text);
97
+ }
98
+ const body = await this.#safeJson(response);
99
+ const error = errorFromResponse(response.status, body, requestId);
100
+ if (attempt < attempts && RETRYABLE_STATUSES.has(response.status)) {
101
+ lastError = error;
102
+ await this.#sleep(this.#backoffMs(attempt, response));
103
+ continue;
104
+ }
105
+ throw error;
106
+ }
107
+ catch (err) {
108
+ // Erreur API déjà typée : la logique de reprise a été appliquée
109
+ // dans le `try` (on n'arrive ici qu'une fois les essais épuisés,
110
+ // ou sur un statut non rejouable).
111
+ if (err instanceof TupayError)
112
+ throw err;
113
+ // Panne réseau, DNS, TLS ou timeout — aucune réponse reçue.
114
+ const connectionError = new TupayConnectionError(err instanceof Error && err.name === 'TimeoutError'
115
+ ? `La requête a dépassé le délai de ${this.#timeout} ms.`
116
+ : `Impossible de joindre l'API Tupay : ${err instanceof Error ? err.message : String(err)}`, err);
117
+ if (attempt >= attempts)
118
+ throw connectionError;
119
+ lastError = connectionError;
120
+ await this.#sleep(this.#backoffMs(attempt));
121
+ }
122
+ }
123
+ throw lastError ?? new TupayError('Requête échouée.', { code: 'server_error' });
124
+ }
125
+ #buildUrl(path, query) {
126
+ const url = new URL(`${this.#baseUrl}/api/v1${path}`);
127
+ for (const [key, value] of Object.entries(query ?? {})) {
128
+ if (value !== undefined)
129
+ url.searchParams.set(key, String(value));
130
+ }
131
+ return url.toString();
132
+ }
133
+ /**
134
+ * Backoff exponentiel avec gigue, pour ne pas faire repartir tous les
135
+ * clients en même temps après un incident. Respecte `Retry-After`
136
+ * quand le serveur l'envoie.
137
+ */
138
+ #backoffMs(attempt, response) {
139
+ const retryAfter = response?.headers.get('retry-after');
140
+ if (retryAfter) {
141
+ const seconds = Number.parseInt(retryAfter, 10);
142
+ if (Number.isFinite(seconds) && seconds >= 0) {
143
+ return Math.min(seconds * 1000, 20_000);
144
+ }
145
+ }
146
+ const base = Math.min(500 * 2 ** (attempt - 1), 8_000);
147
+ return base + Math.random() * 250;
148
+ }
149
+ async #safeJson(response) {
150
+ try {
151
+ return (await response.json());
152
+ }
153
+ catch {
154
+ return null;
155
+ }
156
+ }
157
+ #sleep(ms) {
158
+ return new Promise((resolve) => setTimeout(resolve, ms));
159
+ }
160
+ }
161
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,66 @@
1
+ /**
2
+ * Hiérarchie d'erreurs du SDK.
3
+ *
4
+ * Toutes héritent de `TupayError` : un `catch (err)` unique suffit, et
5
+ * `err instanceof TupayRateLimitError` permet d'affiner si besoin.
6
+ *
7
+ * `code` est le code machine renvoyé par l'API (`validation_error`,
8
+ * `livemode_mismatch`, …). C'est lui qu'il faut tester — le `message`
9
+ * est en français, destiné à un humain, et peut changer sans préavis.
10
+ */
11
+ export interface TupayErrorOptions {
12
+ code: string;
13
+ statusCode?: number;
14
+ requestId?: string;
15
+ cause?: unknown;
16
+ }
17
+ export declare class TupayError extends Error {
18
+ /** Code machine stable renvoyé par l'API. */
19
+ readonly code: string;
20
+ /** Statut HTTP, absent si l'erreur est survenue avant toute réponse. */
21
+ readonly statusCode?: number;
22
+ /** Identifiant de requête, à citer au support. */
23
+ readonly requestId?: string;
24
+ constructor(message: string, options: TupayErrorOptions);
25
+ }
26
+ /** 401 — clé absente, invalide ou révoquée. */
27
+ export declare class TupayAuthenticationError extends TupayError {
28
+ }
29
+ /** 403 — opération interdite dans ce contexte (dont `livemode_mismatch`). */
30
+ export declare class TupayPermissionError extends TupayError {
31
+ }
32
+ /** 404 — ressource inexistante ou hors du périmètre de la clé. */
33
+ export declare class TupayNotFoundError extends TupayError {
34
+ }
35
+ /** 400 / 422 — corps de requête invalide. */
36
+ export declare class TupayValidationError extends TupayError {
37
+ }
38
+ /** 409 — état incompatible (déjà remboursé, limite atteinte). */
39
+ export declare class TupayConflictError extends TupayError {
40
+ }
41
+ /** 429 — 100 req/min par marchand dépassées. */
42
+ export declare class TupayRateLimitError extends TupayError {
43
+ }
44
+ /** 5xx — incident côté Tupay. */
45
+ export declare class TupayApiError extends TupayError {
46
+ }
47
+ /** 502 — le prestataire bancaire a refusé l'opération. */
48
+ export declare class TupayBaasError extends TupayError {
49
+ }
50
+ /** Panne réseau, DNS, TLS ou dépassement du timeout. Aucune réponse reçue. */
51
+ export declare class TupayConnectionError extends TupayError {
52
+ constructor(message: string, cause?: unknown);
53
+ }
54
+ /** La signature d'un webhook n'a pas pu être vérifiée. */
55
+ export declare class TupaySignatureVerificationError extends TupayError {
56
+ constructor(message: string);
57
+ }
58
+ /**
59
+ * Construit l'erreur typée correspondant à une réponse HTTP.
60
+ * Le mapping suit le catalogue documenté dans `docs/api-integration.md`.
61
+ */
62
+ export declare function errorFromResponse(status: number, body: {
63
+ error?: string;
64
+ message?: string;
65
+ } | null, requestId?: string): TupayError;
66
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/errors.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAA;IACZ,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,KAAK,CAAC,EAAE,OAAO,CAAA;CAChB;AAED,qBAAa,UAAW,SAAQ,KAAK;IACnC,6CAA6C;IAC7C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,wEAAwE;IACxE,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAA;IAC5B,kDAAkD;IAClD,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAA;gBAEf,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,iBAAiB;CAOxD;AAED,+CAA+C;AAC/C,qBAAa,wBAAyB,SAAQ,UAAU;CAAG;AAE3D,6EAA6E;AAC7E,qBAAa,oBAAqB,SAAQ,UAAU;CAAG;AAEvD,kEAAkE;AAClE,qBAAa,kBAAmB,SAAQ,UAAU;CAAG;AAErD,6CAA6C;AAC7C,qBAAa,oBAAqB,SAAQ,UAAU;CAAG;AAEvD,iEAAiE;AACjE,qBAAa,kBAAmB,SAAQ,UAAU;CAAG;AAErD,gDAAgD;AAChD,qBAAa,mBAAoB,SAAQ,UAAU;CAAG;AAEtD,iCAAiC;AACjC,qBAAa,aAAc,SAAQ,UAAU;CAAG;AAEhD,0DAA0D;AAC1D,qBAAa,cAAe,SAAQ,UAAU;CAAG;AAEjD,8EAA8E;AAC9E,qBAAa,oBAAqB,SAAQ,UAAU;gBACtC,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,OAAO;CAG7C;AAED,0DAA0D;AAC1D,qBAAa,+BAAgC,SAAQ,UAAU;gBACjD,OAAO,EAAE,MAAM;CAG5B;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,MAAM,EACd,IAAI,EAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,EACjD,SAAS,CAAC,EAAE,MAAM,GACjB,UAAU,CAwBZ"}