@timbro/payments 0.1.0-next.3 → 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/README.md CHANGED
@@ -8,54 +8,51 @@ Payments; an administrator key, which the owner creates and names in the Workspa
8
8
  settings, also administers the business and its Merchant Accounts. Keys are additive: creating another one,
9
9
  of either kind, never retires the keys you already use.
10
10
 
11
- 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.
11
+ Each Payment is identified by an immutable `paymentReference` within the merchant and TEST/LIVE scope. The SDK derives a stable HTTP idempotency key from that identity and retries payment creation a bounded number of times. Optional `source` coordinates preserve an upstream order identity; `saleReference` remains a searchable business label and may repeat.
12
12
 
13
- Payment reads preserve that source, the canonical sale, amount, voluntary tip,
14
- collected `totalAmount`, stable capture allocations with sanitized payment
15
- method summaries, and the fiscal recipient projection needed by an ERP.
13
+ Payment reads preserve that source, the canonical sale, decimal money values,
14
+ Checkout URL, stable capture allocations with sanitized payment method
15
+ summaries, and the fiscal recipient projection needed by an ERP.
16
16
 
17
- Use `amount_only` when the adapter has only a payable amount. This variant cannot request fiscal issuance:
17
+ Use the amount form when the adapter has one payable amount. Declare the tax
18
+ included in that amount; Timbro does not infer a tax rate for an amount-only
19
+ request. Omitting `tax` lets an explicitly configured merchant policy apply or
20
+ returns an input error when no policy covers the sale. Omitting `invoicing`
21
+ means no fiscal issuance is requested:
18
22
 
19
23
  ```ts
20
24
  import { createTimbroPayments } from "@timbro/payments";
21
25
 
22
26
  const timbro = createTimbroPayments({ apiKey: process.env.TIMBRO_PAYMENTS_SECRET_KEY });
23
27
  const payment = await timbro.payments.create({
24
- idempotencyKey: "create-order-123",
25
- source: { id: "odoo:pos.order:123", version: "7" },
26
- sale: {
27
- kind: "amount_only",
28
- reference: "order-123",
29
- description: "Consumo en el comercio",
30
- amount: { currency: "DOP", value: "1250.00" },
31
- },
32
- invoicing: null,
28
+ paymentReference: "order-123-payment-1",
29
+ amount: { currency: "DOP", value: "1250.00" },
30
+ description: "Consumo en el comercio",
31
+ tax: { includedAmount: "190.68" },
33
32
  });
33
+ return Response.redirect(payment.checkout.url);
34
34
  ```
35
35
 
36
- Use `itemized` when the adapter has commercial lines. Each line carries its fiscal description and exact decimal price; optional `product` data is presentation metadata:
36
+ Use the itemized form when the adapter has commercial lines. Each line carries
37
+ its description, exact decimal price, and tax treatment. Fiscal item fields
38
+ become required when invoicing needs them:
37
39
 
38
40
  ```ts
39
41
  await timbro.payments.create({
40
- idempotencyKey: "create-coffee-126",
42
+ paymentReference: "order-126-payment-1",
41
43
  source: { id: "odoo:pos.order:126", version: "3" },
42
- sale: { kind: "itemized", reference: "coffee-126", currency: "DOP",
43
- items: [{
44
- id: "coffee", description: "Café",
45
- product: { id: "product-7", sku: "CAFE-7", imageUrl: "https://merchant.example/cafe.png" },
46
- quantity: "1", unit: "unit", unitPrice: "118.00", kind: "good",
47
- tax: { kind: "itbis_18", included: true },
48
- }],
49
- totals: { net: "100.00", tax: "18.00", legalTip: "0.00", total: "118.00" } },
50
- invoicing: null,
44
+ currency: "DOP",
45
+ items: [{ description: "Café", quantity: "2", unitPrice: "118.00",
46
+ tax: { kind: "itbis_18", included: true } }],
47
+ expectedTotals: { net: "200.00", tax: "36.00", legalTip: "0.00", total: "236.00" },
51
48
  });
49
+ ```
52
50
 
53
- `sale.totals.total` is the commercial payable amount. A `non_billable` line may
54
- be collected without entering the fiscal amount. The returned breakdown names
55
- the invariant explicitly: `payableTotalMinorUnits` equals
56
- `fiscalDeclaredTotalMinorUnits + nonBillableMinorUnits`. Legal tip is included
57
- once in the fiscal declared total; voluntary tip is added only to collected
58
- `totalAmount`.
51
+ Timbro computes line rounding and totals once. `expectedTotals` lets the caller
52
+ assert agreement with its accounting snapshot. Product IDs, line IDs, quantity,
53
+ unit, and good/service kind are optional for payment-only lines; invoicing
54
+ requires the fiscal details it consumes. Money fields on Payment and Refund
55
+ resources use `{ currency, value }` decimal strings.
59
56
 
60
57
  Refunds are durable full or partial resources with idempotent create, retrieve,
61
58
  and list operations. Provider outcome and fiscal note coordination are separate.
@@ -63,11 +60,9 @@ The Gateway reserves captured allocation balances before provider work begins:
63
60
 
64
61
  ```ts
65
62
  const refund = await timbro.refunds.create({
66
- idempotencyKey: "refund-order-124-1",
67
63
  paymentId: payment.id,
68
- source: { id: "odoo:pos.order:124:refund:1", version: "1" },
69
- reference: "order-124-return-1",
70
- amount: { currency: "DOP", minorUnits: 12500 },
64
+ refundReference: "order-124-return-1",
65
+ amount: { currency: "DOP", value: "125.00" },
71
66
  // Optional for split Payments; omit to reserve across every captured allocation.
72
67
  allocationIds: payment.capturedPayment?.allocations.map(({ id }) => id),
73
68
  });
@@ -75,10 +70,13 @@ const refund = await timbro.refunds.create({
75
70
 
76
71
  The Refund response keeps `allocationIds` and exact `allocationAmounts` so an
77
72
  ERP can reconcile one or more split-payer allocations without exceeding any
78
- captured minor-unit balance. The Refund row owns the durable provider binding,
79
- submission fence, and recovery schedule. Test-mode CardNet full refunds use the
80
- Ztrans submission and same-key inquiry path. Partial, split, live-mode, Azul,
81
- and sandbox paths remain `operator_required` before remote money movement.
73
+ captured balance. The Refund row owns the durable provider binding,
74
+ submission fence, and recovery schedule. Full, unsplit DOP TEST Azul card refunds
75
+ use the original capture credentials and tax, and confirm approval through
76
+ `VerifyPayment` on a unique refund correlation. TEST CardNet full refunds retain
77
+ the Ztrans submission and same-key inquiry path, pending provider qualification
78
+ in [issue #587](https://github.com/indexa-labs/checkout/issues/587). Partial, split,
79
+ live-mode, wallet, and sandbox paths remain `operator_required` before remote money movement.
82
80
 
83
81
  An authenticated ERP can attach Timbro's accepted E34 fiscal credit note after
84
82
  the refund request is recorded. This advances only `fiscalCoordination`; it does
@@ -116,27 +114,43 @@ money is outside cancellation and requires a separate refund policy.
116
114
  ```ts
117
115
  const canceled = await timbro.payments.cancel({
118
116
  paymentId: payment.id,
119
- idempotencyKey: "cancel-order-123",
120
117
  });
121
118
  ```
122
119
 
123
120
  `merchantCheckoutPolicy.retrieve()` reads tenant- and mode-scoped defaults and
124
- constraints. Administrators update them through the authenticated administrative
125
- `PUT /v1/merchant_checkout_policy` operation with the expected revision; ordinary
126
- merchant secret keys cannot update policy. Accepted configuration is retained with the Payment;
127
- later policy edits do not reroute existing Payments. Account readiness can still
128
- withdraw permission to start a provider effect.
121
+ constraints. Administrators replace them through a revision-checked `PUT`;
122
+ ordinary merchant secret keys cannot update policy. The optional `defaults.tax`
123
+ selects an explicit inclusive tax treatment for amount-only requests:
124
+
125
+ ```ts
126
+ const admin = createTimbroPayments({ apiKey: process.env.TIMBRO_ADMIN_KEY });
127
+ const policy = await admin.merchantCheckoutPolicy.retrieve();
128
+ await admin.merchantCheckoutPolicy.update({
129
+ expectedRevision: policy.revision,
130
+ defaults: {
131
+ ...policy.defaults,
132
+ tax: { kind: "itbis_18", included: true },
133
+ },
134
+ constraints: policy.constraints,
135
+ });
136
+ ```
137
+
138
+ Accepted configuration is retained with the Payment; later policy edits do not
139
+ reroute existing Payments. Account readiness can still withdraw permission to
140
+ start a provider effect.
129
141
 
130
142
  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.
131
143
 
132
144
  ```ts
133
145
  await timbro.payments.create({
134
- idempotencyKey: "create-required-order-124",
146
+ paymentReference: "order-124-payment-1",
135
147
  source: { id: "odoo:pos.order:124", version: "9" },
136
- sale: { kind: "itemized", reference: "order-124", currency: "DOP", items: [{
148
+ currency: "DOP",
149
+ items: [{
137
150
  id: "meal", description: "Meal", quantity: "1", unit: "unit", unitPrice: "1250.00", kind: "good",
138
151
  tax: { kind: "itbis_18", included: true },
139
- }], totals: { net: "1059.32", tax: "190.68", legalTip: "0.00", total: "1250.00" } },
152
+ }],
153
+ expectedTotals: { net: "1059.32", tax: "190.68", legalTip: "0.00", total: "1250.00" },
140
154
  invoicing: { mode: "required", type: "consumer", merchantInvoiceNumber: "INV-124", merchantOrderNumber: "124" },
141
155
  });
142
156
  ```
@@ -149,10 +163,9 @@ Refunds use one resource for the provider money movement and the separate fiscal
149
163
  const refund = await timbro.refunds.create({
150
164
  paymentId: payment.id,
151
165
  source: { id: "odoo:pos.order:124:return:1", version: "1" },
152
- reference: "RETURN-124-1",
153
- amount: { currency: "DOP", minorUnits: 50_000 },
166
+ refundReference: "RETURN-124-1",
167
+ amount: { currency: "DOP", value: "500.00" },
154
168
  allocationIds: [payment.capturedPayment!.allocations[0]!.id],
155
- idempotencyKey: "refund-order-124-return-1",
156
169
  });
157
170
 
158
171
  if (refund.providerOutcome.status === "succeeded") {
@@ -166,9 +179,15 @@ if (refund.providerOutcome.status === "succeeded") {
166
179
  }
167
180
  ```
168
181
 
169
- 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.
182
+ `timbro.refunds.create()` also accepts an omitted `amount`, which reserves the
183
+ remaining refundable balance once. Repeating the same `refundReference` returns that
184
+ same reservation. `processing` means provider submission can still begin,
185
+ `verifying` means an ambiguous submission is being checked without
186
+ resubmission, and `operator_required` means staff must complete or reconcile
187
+ the money movement. Provider outcome and fiscal E34 acceptance stay
188
+ independent.
170
189
 
171
- 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.
190
+ Full, unsplit DOP TEST Azul card refunds submit against the original captured transaction and accepted tax, then confirm the refund amount through `VerifyPayment`. Azul allows one refund per settled transaction within six months; this implementation supports a full refund. CardNet Ztrans full refunds have deterministic recovery tests, while provider refund, inquiry, and settlement qualification remains open in [issue #587](https://github.com/indexa-labs/checkout/issues/587). Partial, split, live-mode, wallet, and sandbox refund paths reserve the requested captured balance and return `operator_required` before remote provider I/O.
172
191
 
173
192
  Before collecting the rest of a merchant's onboarding details, verify its RNC
174
193
  against the current DGII directory snapshot. The operation is read-only and