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.
- package/README.md +358 -0
- package/dist/cjs/client.d.ts +48 -0
- package/dist/cjs/client.d.ts.map +1 -0
- package/dist/cjs/client.js +165 -0
- package/dist/cjs/client.js.map +1 -0
- package/dist/cjs/errors.d.ts +66 -0
- package/dist/cjs/errors.d.ts.map +1 -0
- package/dist/cjs/errors.js +105 -0
- package/dist/cjs/errors.js.map +1 -0
- package/dist/cjs/index.d.ts +86 -0
- package/dist/cjs/index.d.ts.map +1 -0
- package/dist/cjs/index.js +110 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/cjs/liste.d.ts +54 -0
- package/dist/cjs/liste.d.ts.map +1 -0
- package/dist/cjs/liste.js +36 -0
- package/dist/cjs/liste.js.map +1 -0
- package/dist/cjs/package.json +3 -0
- package/dist/cjs/resources/events.d.ts +64 -0
- package/dist/cjs/resources/events.d.ts.map +1 -0
- package/dist/cjs/resources/events.js +104 -0
- package/dist/cjs/resources/events.js.map +1 -0
- package/dist/cjs/resources/merchants.d.ts +19 -0
- package/dist/cjs/resources/merchants.d.ts.map +1 -0
- package/dist/cjs/resources/merchants.js +29 -0
- package/dist/cjs/resources/merchants.js.map +1 -0
- package/dist/cjs/resources/payment-intents.d.ts +29 -0
- package/dist/cjs/resources/payment-intents.d.ts.map +1 -0
- package/dist/cjs/resources/payment-intents.js +22 -0
- package/dist/cjs/resources/payment-intents.js.map +1 -0
- package/dist/cjs/resources/payment-links.d.ts +38 -0
- package/dist/cjs/resources/payment-links.d.ts.map +1 -0
- package/dist/cjs/resources/payment-links.js +56 -0
- package/dist/cjs/resources/payment-links.js.map +1 -0
- package/dist/cjs/resources/refunds.d.ts +27 -0
- package/dist/cjs/resources/refunds.d.ts.map +1 -0
- package/dist/cjs/resources/refunds.js +33 -0
- package/dist/cjs/resources/refunds.js.map +1 -0
- package/dist/cjs/resources/test-helpers.d.ts +42 -0
- package/dist/cjs/resources/test-helpers.d.ts.map +1 -0
- package/dist/cjs/resources/test-helpers.js +50 -0
- package/dist/cjs/resources/test-helpers.js.map +1 -0
- package/dist/cjs/resources/transactions.d.ts +29 -0
- package/dist/cjs/resources/transactions.d.ts.map +1 -0
- package/dist/cjs/resources/transactions.js +65 -0
- package/dist/cjs/resources/transactions.js.map +1 -0
- package/dist/cjs/resources/webhook-endpoints.d.ts +43 -0
- package/dist/cjs/resources/webhook-endpoints.d.ts.map +1 -0
- package/dist/cjs/resources/webhook-endpoints.js +71 -0
- package/dist/cjs/resources/webhook-endpoints.js.map +1 -0
- package/dist/cjs/types.d.ts +166 -0
- package/dist/cjs/types.d.ts.map +1 -0
- package/dist/cjs/types.js +10 -0
- package/dist/cjs/types.js.map +1 -0
- package/dist/cjs/webhooks.d.ts +31 -0
- package/dist/cjs/webhooks.d.ts.map +1 -0
- package/dist/cjs/webhooks.js +81 -0
- package/dist/cjs/webhooks.js.map +1 -0
- package/dist/esm/client.d.ts +48 -0
- package/dist/esm/client.d.ts.map +1 -0
- package/dist/esm/client.js +161 -0
- package/dist/esm/client.js.map +1 -0
- package/dist/esm/errors.d.ts +66 -0
- package/dist/esm/errors.d.ts.map +1 -0
- package/dist/esm/errors.js +90 -0
- package/dist/esm/errors.js.map +1 -0
- package/dist/esm/index.d.ts +86 -0
- package/dist/esm/index.d.ts.map +1 -0
- package/dist/esm/index.js +90 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/liste.d.ts +54 -0
- package/dist/esm/liste.d.ts.map +1 -0
- package/dist/esm/liste.js +32 -0
- package/dist/esm/liste.js.map +1 -0
- package/dist/esm/package.json +3 -0
- package/dist/esm/resources/events.d.ts +64 -0
- package/dist/esm/resources/events.d.ts.map +1 -0
- package/dist/esm/resources/events.js +100 -0
- package/dist/esm/resources/events.js.map +1 -0
- package/dist/esm/resources/merchants.d.ts +19 -0
- package/dist/esm/resources/merchants.d.ts.map +1 -0
- package/dist/esm/resources/merchants.js +25 -0
- package/dist/esm/resources/merchants.js.map +1 -0
- package/dist/esm/resources/payment-intents.d.ts +29 -0
- package/dist/esm/resources/payment-intents.d.ts.map +1 -0
- package/dist/esm/resources/payment-intents.js +18 -0
- package/dist/esm/resources/payment-intents.js.map +1 -0
- package/dist/esm/resources/payment-links.d.ts +38 -0
- package/dist/esm/resources/payment-links.d.ts.map +1 -0
- package/dist/esm/resources/payment-links.js +52 -0
- package/dist/esm/resources/payment-links.js.map +1 -0
- package/dist/esm/resources/refunds.d.ts +27 -0
- package/dist/esm/resources/refunds.d.ts.map +1 -0
- package/dist/esm/resources/refunds.js +29 -0
- package/dist/esm/resources/refunds.js.map +1 -0
- package/dist/esm/resources/test-helpers.d.ts +42 -0
- package/dist/esm/resources/test-helpers.d.ts.map +1 -0
- package/dist/esm/resources/test-helpers.js +46 -0
- package/dist/esm/resources/test-helpers.js.map +1 -0
- package/dist/esm/resources/transactions.d.ts +29 -0
- package/dist/esm/resources/transactions.d.ts.map +1 -0
- package/dist/esm/resources/transactions.js +61 -0
- package/dist/esm/resources/transactions.js.map +1 -0
- package/dist/esm/resources/webhook-endpoints.d.ts +43 -0
- package/dist/esm/resources/webhook-endpoints.d.ts.map +1 -0
- package/dist/esm/resources/webhook-endpoints.js +67 -0
- package/dist/esm/resources/webhook-endpoints.js.map +1 -0
- package/dist/esm/types.d.ts +166 -0
- package/dist/esm/types.d.ts.map +1 -0
- package/dist/esm/types.js +9 -0
- package/dist/esm/types.js.map +1 -0
- package/dist/esm/webhooks.d.ts +31 -0
- package/dist/esm/webhooks.d.ts.map +1 -0
- package/dist/esm/webhooks.js +77 -0
- package/dist/esm/webhooks.js.map +1 -0
- 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,9 @@
|
|
|
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 {};
|
|
9
|
+
//# 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,77 @@
|
|
|
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 { createHmac, timingSafeEqual } from 'node:crypto';
|
|
13
|
+
import { TupaySignatureVerificationError } from './errors.js';
|
|
14
|
+
/** Écart maximum toléré entre l'horodatage signé et l'heure locale. */
|
|
15
|
+
export const DEFAULT_TOLERANCE_SECONDS = 300;
|
|
16
|
+
function parseHeader(header) {
|
|
17
|
+
let timestamp = null;
|
|
18
|
+
let v1 = null;
|
|
19
|
+
for (const part of header.split(',')) {
|
|
20
|
+
const separator = part.indexOf('=');
|
|
21
|
+
if (separator === -1)
|
|
22
|
+
continue;
|
|
23
|
+
const key = part.slice(0, separator).trim();
|
|
24
|
+
const value = part.slice(separator + 1).trim();
|
|
25
|
+
if (key === 't') {
|
|
26
|
+
const parsed = Number.parseInt(value, 10);
|
|
27
|
+
if (Number.isFinite(parsed))
|
|
28
|
+
timestamp = parsed;
|
|
29
|
+
}
|
|
30
|
+
else if (key === 'v1') {
|
|
31
|
+
v1 = value;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return timestamp === null || v1 === null ? null : { timestamp, v1 };
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Vérifie la signature et renvoie l'événement typé.
|
|
38
|
+
*
|
|
39
|
+
* @param payload corps BRUT de la requête (string ou Buffer)
|
|
40
|
+
* @param header valeur de l'en-tête `Tupay-Signature`
|
|
41
|
+
* @param secret secret de l'endpoint (`whsec_…`)
|
|
42
|
+
* @throws {TupaySignatureVerificationError} si la signature ou l'horodatage
|
|
43
|
+
* est invalide — répondez alors 400 sans traiter l'événement.
|
|
44
|
+
*/
|
|
45
|
+
export function constructEvent(payload, header, secret, options = {}) {
|
|
46
|
+
if (!secret) {
|
|
47
|
+
throw new TupaySignatureVerificationError('Secret de webhook manquant. Récupérez-le à la création de l’endpoint (whsec_…).');
|
|
48
|
+
}
|
|
49
|
+
if (!header) {
|
|
50
|
+
throw new TupaySignatureVerificationError('En-tête Tupay-Signature absent de la requête.');
|
|
51
|
+
}
|
|
52
|
+
const parts = parseHeader(header);
|
|
53
|
+
if (!parts) {
|
|
54
|
+
throw new TupaySignatureVerificationError('En-tête Tupay-Signature malformé — format attendu : t=<unix>,v1=<hex>.');
|
|
55
|
+
}
|
|
56
|
+
const tolerance = options.toleranceSeconds ?? DEFAULT_TOLERANCE_SECONDS;
|
|
57
|
+
const now = options.nowSeconds ?? Math.floor(Date.now() / 1000);
|
|
58
|
+
if (tolerance > 0 && Math.abs(now - parts.timestamp) > tolerance) {
|
|
59
|
+
throw new TupaySignatureVerificationError(`Horodatage hors tolérance (${tolerance} s) — possible rejeu d’une livraison capturée.`);
|
|
60
|
+
}
|
|
61
|
+
const raw = typeof payload === 'string' ? payload : payload.toString('utf8');
|
|
62
|
+
const expected = createHmac('sha256', secret)
|
|
63
|
+
.update(`${parts.timestamp}.${raw}`, 'utf8')
|
|
64
|
+
.digest('hex');
|
|
65
|
+
// Longueurs comparées d'abord : timingSafeEqual lève si elles diffèrent.
|
|
66
|
+
if (expected.length !== parts.v1.length ||
|
|
67
|
+
!timingSafeEqual(Buffer.from(expected, 'utf8'), Buffer.from(parts.v1, 'utf8'))) {
|
|
68
|
+
throw new TupaySignatureVerificationError('Signature invalide — la charge utile ne correspond pas au secret de cet endpoint.');
|
|
69
|
+
}
|
|
70
|
+
try {
|
|
71
|
+
return JSON.parse(raw);
|
|
72
|
+
}
|
|
73
|
+
catch {
|
|
74
|
+
throw new TupaySignatureVerificationError('Signature valide mais corps illisible : le JSON est malformé.');
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
//# sourceMappingURL=webhooks.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"webhooks.js","sourceRoot":"","sources":["../../src/webhooks.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "tupay",
|
|
3
|
+
"version": "0.5.2",
|
|
4
|
+
"description": "SDK Node officiel de Tupay : encaissement en XPF pour la Polynésie française.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"engines": {
|
|
8
|
+
"node": ">=18"
|
|
9
|
+
},
|
|
10
|
+
"main": "./dist/cjs/index.js",
|
|
11
|
+
"module": "./dist/esm/index.js",
|
|
12
|
+
"types": "./dist/esm/index.d.ts",
|
|
13
|
+
"exports": {
|
|
14
|
+
".": {
|
|
15
|
+
"import": {
|
|
16
|
+
"types": "./dist/esm/index.d.ts",
|
|
17
|
+
"default": "./dist/esm/index.js"
|
|
18
|
+
},
|
|
19
|
+
"require": {
|
|
20
|
+
"types": "./dist/cjs/index.d.ts",
|
|
21
|
+
"default": "./dist/cjs/index.js"
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
"./package.json": "./package.json"
|
|
25
|
+
},
|
|
26
|
+
"files": [
|
|
27
|
+
"dist",
|
|
28
|
+
"README.md"
|
|
29
|
+
],
|
|
30
|
+
"scripts": {
|
|
31
|
+
"build": "npm run clean && npm run build:esm && npm run build:cjs && node scripts/finalize-build.mjs",
|
|
32
|
+
"build:esm": "tsc -p tsconfig.esm.json",
|
|
33
|
+
"build:cjs": "tsc -p tsconfig.cjs.json",
|
|
34
|
+
"clean": "node -e \"require('node:fs').rmSync('dist',{recursive:true,force:true})\"",
|
|
35
|
+
"typecheck": "tsc --noEmit -p tsconfig.json",
|
|
36
|
+
"test": "vitest run",
|
|
37
|
+
"test:watch": "vitest",
|
|
38
|
+
"prepublishOnly": "npm run build && npm test"
|
|
39
|
+
},
|
|
40
|
+
"keywords": [
|
|
41
|
+
"tupay",
|
|
42
|
+
"paiement",
|
|
43
|
+
"xpf",
|
|
44
|
+
"polynesie",
|
|
45
|
+
"payment",
|
|
46
|
+
"gateway"
|
|
47
|
+
],
|
|
48
|
+
"devDependencies": {
|
|
49
|
+
"@types/node": "^22.10.2",
|
|
50
|
+
"typescript": "^5.7.2",
|
|
51
|
+
"vitest": "^3.2.6"
|
|
52
|
+
}
|
|
53
|
+
}
|