@timbro/payments 0.1.0-next.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/README.md +178 -0
- package/dist/generated/openapi.d.ts +17801 -0
- package/dist/generated/openapi.d.ts.map +1 -0
- package/dist/generated/openapi.js +6 -0
- package/dist/generated/openapi.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/dist/payments-client.d.ts +495 -0
- package/dist/payments-client.d.ts.map +1 -0
- package/dist/payments-client.js +670 -0
- package/dist/payments-client.js.map +1 -0
- package/dist/webhook-signature.d.ts +13 -0
- package/dist/webhook-signature.d.ts.map +1 -0
- package/dist/webhook-signature.js +57 -0
- package/dist/webhook-signature.js.map +1 -0
- package/package.json +40 -0
package/README.md
ADDED
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
# `@timbro/payments`
|
|
2
|
+
|
|
3
|
+
Node 24 ESM SDK for merchant Payments and their payment-linked fiscal documents.
|
|
4
|
+
|
|
5
|
+
Each Payment create request supplies one immutable `source` coordinate and one tagged `sale` snapshot. `source.id` and `source.version` remain stable across credential rotation and retries; `idempotencyKey` remains the HTTP command retry key.
|
|
6
|
+
|
|
7
|
+
Payment reads preserve that source, the canonical sale, amount, voluntary tip,
|
|
8
|
+
collected `totalAmount`, stable capture allocations with sanitized payment
|
|
9
|
+
method summaries, and the fiscal recipient projection needed by an ERP.
|
|
10
|
+
|
|
11
|
+
Use `amount_only` when the adapter has only a payable amount. This variant cannot request fiscal issuance:
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
import { createTimbroPayments } from "@timbro/payments";
|
|
15
|
+
|
|
16
|
+
const timbro = createTimbroPayments({ secretKey: process.env.TIMBRO_PAYMENTS_SECRET_KEY });
|
|
17
|
+
const payment = await timbro.payments.create({
|
|
18
|
+
idempotencyKey: "create-order-123",
|
|
19
|
+
source: { id: "odoo:pos.order:123", version: "7" },
|
|
20
|
+
sale: {
|
|
21
|
+
kind: "amount_only",
|
|
22
|
+
reference: "order-123",
|
|
23
|
+
description: "Consumo en el comercio",
|
|
24
|
+
amount: { currency: "DOP", value: "1250.00" },
|
|
25
|
+
},
|
|
26
|
+
invoicing: null,
|
|
27
|
+
});
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Use `itemized` when the adapter has commercial lines. Each line carries its fiscal description and exact decimal price; optional `product` data is presentation metadata:
|
|
31
|
+
|
|
32
|
+
```ts
|
|
33
|
+
await timbro.payments.create({
|
|
34
|
+
idempotencyKey: "create-coffee-126",
|
|
35
|
+
source: { id: "odoo:pos.order:126", version: "3" },
|
|
36
|
+
sale: { kind: "itemized", reference: "coffee-126", currency: "DOP",
|
|
37
|
+
items: [{
|
|
38
|
+
id: "coffee", description: "Café",
|
|
39
|
+
product: { id: "product-7", sku: "CAFE-7", imageUrl: "https://merchant.example/cafe.png" },
|
|
40
|
+
quantity: "1", unit: "unit", unitPrice: "118.00", kind: "good",
|
|
41
|
+
tax: { kind: "itbis_18", included: true },
|
|
42
|
+
}],
|
|
43
|
+
totals: { net: "100.00", tax: "18.00", legalTip: "0.00", total: "118.00" } },
|
|
44
|
+
invoicing: null,
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
`sale.totals.total` is the commercial payable amount. A `non_billable` line may
|
|
48
|
+
be collected without entering the fiscal amount. The returned breakdown names
|
|
49
|
+
the invariant explicitly: `payableTotalMinorUnits` equals
|
|
50
|
+
`fiscalDeclaredTotalMinorUnits + nonBillableMinorUnits`. Legal tip is included
|
|
51
|
+
once in the fiscal declared total; voluntary tip is added only to collected
|
|
52
|
+
`totalAmount`.
|
|
53
|
+
|
|
54
|
+
Refunds are durable full or partial resources with idempotent create, retrieve,
|
|
55
|
+
and list operations. Provider outcome and fiscal note coordination are separate.
|
|
56
|
+
The Gateway reserves captured allocation balances before provider work begins:
|
|
57
|
+
|
|
58
|
+
```ts
|
|
59
|
+
const refund = await timbro.refunds.create({
|
|
60
|
+
idempotencyKey: "refund-order-124-1",
|
|
61
|
+
paymentId: payment.id,
|
|
62
|
+
source: { id: "odoo:pos.order:124:refund:1", version: "1" },
|
|
63
|
+
reference: "order-124-return-1",
|
|
64
|
+
amount: { currency: "DOP", minorUnits: 12500 },
|
|
65
|
+
// Optional for split Payments; omit to reserve across every captured allocation.
|
|
66
|
+
allocationIds: payment.capturedPayment?.allocations.map(({ id }) => id),
|
|
67
|
+
});
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
The Refund response keeps `allocationIds` and exact `allocationAmounts` so an
|
|
71
|
+
ERP can reconcile one or more split-payer allocations without exceeding any
|
|
72
|
+
captured minor-unit balance. The Refund row owns the durable provider binding,
|
|
73
|
+
submission fence, and recovery schedule. Test-mode CardNet full refunds use the
|
|
74
|
+
Ztrans submission and same-key inquiry path. Partial, split, live-mode, Azul,
|
|
75
|
+
and sandbox paths remain `operator_required` before remote money movement.
|
|
76
|
+
|
|
77
|
+
An authenticated ERP can attach Timbro's accepted E34 fiscal credit note after
|
|
78
|
+
the refund request is recorded. This advances only `fiscalCoordination`; it does
|
|
79
|
+
not claim that provider money movement succeeded:
|
|
80
|
+
|
|
81
|
+
```ts
|
|
82
|
+
await timbro.refunds.attachFiscalCoordination({
|
|
83
|
+
refundId: refund.id,
|
|
84
|
+
timbroFiscalDocumentId: "timbro-operation-01h123456789abcdefgh",
|
|
85
|
+
eNcf: "E3400000001",
|
|
86
|
+
acceptedAt: "2026-09-11T00:00:00.000Z",
|
|
87
|
+
idempotencyKey: "refund-fiscal-note-124-1",
|
|
88
|
+
});
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Optional `merchantAccountId` pins an authorized account for the Payment.
|
|
92
|
+
`merchantAccounts.retrieveCheckoutCapabilities(id)` discovers its supported
|
|
93
|
+
Checkout features. Preferences can restrict available methods, but cannot grant
|
|
94
|
+
capabilities or bypass merchant policy. Empty `wallets` explicitly disables both
|
|
95
|
+
wallets; omitted preferences retain the merchant's applicable defaults.
|
|
96
|
+
`testOptions.threeDs` selects authentication for a test Payment only and cannot
|
|
97
|
+
disable production authentication. CardNet requires its supported 3DS flow.
|
|
98
|
+
|
|
99
|
+
Cancel an unpaid Payment from the merchant backend with a stable command key.
|
|
100
|
+
The operation invalidates prepared Checkout submissions immediately. Split
|
|
101
|
+
authorizations enter provider release and the returned Payment remains
|
|
102
|
+
`releasing` until every void is confirmed; retrieve the Payment to observe the
|
|
103
|
+
authoritative terminal `canceled` state. The gateway emits one ordered
|
|
104
|
+
`payment.canceled` webhook only after that terminal state is committed. A same-key `202` cancellation replay is
|
|
105
|
+
intentionally stale while release is pending; polling the Payment is the
|
|
106
|
+
correctness authority. A submitted or ambiguous unsplit attempt returns a
|
|
107
|
+
conflict and stays under correlation reconciliation. Captured
|
|
108
|
+
money is outside cancellation and requires a separate refund policy.
|
|
109
|
+
|
|
110
|
+
```ts
|
|
111
|
+
const canceled = await timbro.payments.cancel({
|
|
112
|
+
paymentId: payment.id,
|
|
113
|
+
idempotencyKey: "cancel-order-123",
|
|
114
|
+
});
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
`merchantCheckoutPolicy.retrieve()` reads tenant- and mode-scoped defaults and
|
|
118
|
+
constraints. Administrators update them through the authenticated administrative
|
|
119
|
+
`PUT /v1/merchant_checkout_policy` operation with the expected revision; ordinary
|
|
120
|
+
merchant secret keys cannot update policy. Accepted configuration is retained with the Payment;
|
|
121
|
+
later policy edits do not reroute existing Payments. Account readiness can still
|
|
122
|
+
withdraw permission to start a provider effect.
|
|
123
|
+
|
|
124
|
+
Use `mode: "required"` when Checkout must remain open until issuance. Required and deferred invoicing require the merchant's real invoice and order references. `externally_coordinated` records the payer recipient choice and lets the authenticated ERP supply those identifiers later.
|
|
125
|
+
|
|
126
|
+
```ts
|
|
127
|
+
await timbro.payments.create({
|
|
128
|
+
idempotencyKey: "create-required-order-124",
|
|
129
|
+
source: { id: "odoo:pos.order:124", version: "9" },
|
|
130
|
+
sale: { kind: "itemized", reference: "order-124", currency: "DOP", items: [{
|
|
131
|
+
id: "meal", description: "Meal", quantity: "1", unit: "unit", unitPrice: "1250.00", kind: "good",
|
|
132
|
+
tax: { kind: "itbis_18", included: true },
|
|
133
|
+
}], totals: { net: "1059.32", tax: "190.68", legalTip: "0.00", total: "1250.00" } },
|
|
134
|
+
invoicing: { mode: "required", type: "consumer", merchantInvoiceNumber: "INV-124", merchantOrderNumber: "124" },
|
|
135
|
+
});
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
Use `mode: "deferred"` when Checkout owns issuance and retries, but the paid receipt may be shown before issuance finishes. The same complete immutable sale and invoicing intent is captured before provider I/O.
|
|
139
|
+
|
|
140
|
+
Refunds use one resource for the provider money movement and the separate fiscal-note coordination state. An ERP or POS supplies its immutable source coordinate and exact captured allocation attribution:
|
|
141
|
+
|
|
142
|
+
```ts
|
|
143
|
+
const refund = await timbro.refunds.create({
|
|
144
|
+
paymentId: payment.id,
|
|
145
|
+
source: { id: "odoo:pos.order:124:return:1", version: "1" },
|
|
146
|
+
reference: "RETURN-124-1",
|
|
147
|
+
amount: { currency: "DOP", minorUnits: 50_000 },
|
|
148
|
+
allocationIds: [payment.capturedPayment!.allocations[0]!.id],
|
|
149
|
+
idempotencyKey: "refund-order-124-return-1",
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
if (refund.providerOutcome.status === "succeeded") {
|
|
153
|
+
await timbro.refunds.attachFiscalCoordination({
|
|
154
|
+
refundId: refund.id,
|
|
155
|
+
timbroFiscalDocumentId: "fiscal_document_123",
|
|
156
|
+
eNcf: "E340000000001",
|
|
157
|
+
acceptedAt: new Date().toISOString(),
|
|
158
|
+
idempotencyKey: "refund-order-124-return-1-e34",
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
For a full, unsplit Payment, `timbro.payments.refund({ paymentId, idempotencyKey })` is a bodyless convenience command that returns the same Refund resource. `processing` means provider submission can still begin, `verifying` means an ambiguous submission is being checked without resubmission, and `operator_required` means staff must complete or reconcile the money movement. Provider outcome and fiscal E34 acceptance stay independent.
|
|
164
|
+
|
|
165
|
+
CardNet Ztrans full refunds are implemented for test mode with deterministic recovery tests. Current laboratory evidence does not prove CardNet's refund-inquiry semantics or production readiness. Partial, split, live-mode, Azul, and sandbox refund paths reserve the requested captured balance and return `operator_required` before remote provider I/O.
|
|
166
|
+
|
|
167
|
+
Before collecting the rest of a merchant's onboarding details, verify its RNC
|
|
168
|
+
against the current DGII directory snapshot. The operation is read-only and
|
|
169
|
+
returns the canonical taxpayer name for the form to use. It currently requires
|
|
170
|
+
an existing merchant secret key; tenant bootstrap and human administrator
|
|
171
|
+
authentication are separate onboarding work:
|
|
172
|
+
|
|
173
|
+
```ts
|
|
174
|
+
const verification = await timbro.merchantOnboarding.verifyRnc({ rnc: "101000155" });
|
|
175
|
+
// verification.legalName is the Directory's registered name.
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
`@timbro/payments` serves payment consumers. `@timbro/sdk` is the consumer-neutral fiscal SDK for ERPs, POS systems, payment systems, and other direct fiscal integrations. Neither package depends on or re-exports the other.
|