@ambushsoftworks/nestjs-payments-graphql 0.8.0 → 0.10.0-rc.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/CHANGELOG.md +170 -0
- package/README.md +142 -2
- package/dist/constants.d.ts +2 -0
- package/dist/constants.js +3 -1
- package/dist/constants.js.map +1 -1
- package/dist/exceptions/index.d.ts +3 -0
- package/dist/exceptions/index.js +7 -1
- package/dist/exceptions/index.js.map +1 -1
- package/dist/gateways/payment-gateway.interface.d.ts +4 -0
- package/dist/gateways/recurring-payment-gateway.interface.d.ts +14 -0
- package/dist/gateways/stripe/stripe-webhook.controller.d.ts +5 -3
- package/dist/gateways/stripe/stripe-webhook.controller.js +43 -21
- package/dist/gateways/stripe/stripe-webhook.controller.js.map +1 -1
- package/dist/gateways/stripe/stripe.gateway.d.ts +21 -0
- package/dist/gateways/stripe/stripe.gateway.js +140 -9
- package/dist/gateways/stripe/stripe.gateway.js.map +1 -1
- package/dist/gateways/stripe/types.d.ts +1 -0
- package/dist/gateways/stripe/types.js +2 -1
- package/dist/gateways/stripe/types.js.map +1 -1
- package/dist/index.d.ts +4 -3
- package/dist/index.js +7 -3
- package/dist/index.js.map +1 -1
- package/dist/interfaces/payment-event-listener.interface.d.ts +2 -0
- package/dist/interfaces/payment-method-policy.interface.d.ts +11 -0
- package/dist/interfaces/payment-method-policy.interface.js +3 -0
- package/dist/interfaces/payment-method-policy.interface.js.map +1 -0
- package/dist/payments.module.d.ts +3 -0
- package/dist/payments.module.js +10 -0
- package/dist/payments.module.js.map +1 -1
- package/dist/services/customer.service.d.ts +3 -1
- package/dist/services/customer.service.js +2 -1
- package/dist/services/customer.service.js.map +1 -1
- package/dist/services/payment-method.service.d.ts +19 -2
- package/dist/services/payment-method.service.js +151 -12
- package/dist/services/payment-method.service.js.map +1 -1
- package/dist/services/payment.service.js +19 -3
- package/dist/services/payment.service.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -28,6 +28,176 @@ prose for buried obligations. The marker was introduced in v0.4.0 and
|
|
|
28
28
|
|
|
29
29
|
## [Unreleased]
|
|
30
30
|
|
|
31
|
+
## [0.10.0] - 2026-09-29
|
|
32
|
+
|
|
33
|
+
> **Upgrade note.** `PaymentMethodService` takes `ModuleRef` as a third
|
|
34
|
+
> constructor argument. It is optional, so a consumer constructing the service
|
|
35
|
+
> by hand — in tests, mostly — still compiles and behaves exactly as before;
|
|
36
|
+
> without it no policy is resolved. Pass it if you want the policy honoured in a
|
|
37
|
+
> hand-built container. Nothing else changes for an existing consumer, and
|
|
38
|
+
> **absent a registered policy every path behaves exactly as it did in 0.9.0.**
|
|
39
|
+
|
|
40
|
+
0.9.0 let a session say whether the card it kept should become the customer's
|
|
41
|
+
default. This closes the class that switch guards case by case: **the package
|
|
42
|
+
now asks before every default it writes, on every path, including ones not
|
|
43
|
+
written yet.**
|
|
44
|
+
|
|
45
|
+
### Added
|
|
46
|
+
- **`IPaymentMethodPolicy`**, with the `PAYMENT_METHOD_POLICY` token and the
|
|
47
|
+
`paymentMethodPolicyInstance` module option. `mayBecomeDefault` is asked
|
|
48
|
+
before the provider is called, with the `trigger` that caused the write —
|
|
49
|
+
`explicit`, `setup` or `payment` — so a consumer can answer differently by
|
|
50
|
+
origin.
|
|
51
|
+
|
|
52
|
+
It exists because the knowledge and the write live in different places. A
|
|
53
|
+
consumer holding cards belonging to third parties knows which card is whose
|
|
54
|
+
and had already written guards on its own mutations; the package writes the
|
|
55
|
+
default inside webhook handling, where those guards never run. Rather than
|
|
56
|
+
teach the package what a third party is, it asks.
|
|
57
|
+
|
|
58
|
+
- **`PaymentMethodDefaultRefusedException`** (`PAYMENT_METHOD_DEFAULT_REFUSED`),
|
|
59
|
+
thrown when a policy refuses an `explicit` request. Someone clicked a button
|
|
60
|
+
and is waiting; a silent no-op reads as success while the card does not stick.
|
|
61
|
+
The automatic paths log and leave the default alone instead — they run inside
|
|
62
|
+
a webhook, where throwing answers 500 and the provider redelivers for days.
|
|
63
|
+
|
|
64
|
+
- **`paymentMethodPolicyTimeoutMs`** (default `5000`) and the
|
|
65
|
+
`PAYMENT_METHOD_POLICY_TIMEOUT` token.
|
|
66
|
+
|
|
67
|
+
- **`resolveSetupPaymentMethod` on `RecurringPaymentGateway`** (optional):
|
|
68
|
+
reports the card a completed setup session collected, and whether the session
|
|
69
|
+
asked for it to be adopted, **without writing anything**. A setup session only
|
|
70
|
+
reveals its card on completion, so `adoptSetupPaymentMethod` resolved and
|
|
71
|
+
adopted in one call and left no point at which a policy could be consulted.
|
|
72
|
+
That method now builds on this one, so there is a single reading of the
|
|
73
|
+
session's answer.
|
|
74
|
+
|
|
75
|
+
### Changed
|
|
76
|
+
- **A policy that throws, rejects or times out is treated as a refusal**, logged
|
|
77
|
+
at error level. This is the opposite of every other consumer hook in the
|
|
78
|
+
package — a branding resolver or a charge-options resolver that fails lets the
|
|
79
|
+
payment proceed, because the cost of proceeding is cosmetic. The cost of
|
|
80
|
+
proceeding here is charging the wrong person. **Fail open for cosmetic, fail
|
|
81
|
+
closed for authority.**
|
|
82
|
+
|
|
83
|
+
The timeout is the only one in the package, and the inconsistency is
|
|
84
|
+
deliberate: everywhere else the safe answer to an unanswerable question is
|
|
85
|
+
"proceed", so a timeout could only cause harm.
|
|
86
|
+
|
|
87
|
+
- **A gateway that cannot be gated refuses rather than proceeds.** With a policy
|
|
88
|
+
registered, a setup-session adoption on a gateway without
|
|
89
|
+
`resolveSetupPaymentMethod` is refused and logged with what to implement.
|
|
90
|
+
Without a policy, the legacy path is used unchanged.
|
|
91
|
+
|
|
92
|
+
### Fixed
|
|
93
|
+
- **`PaidInvoiceEvent`'s card ids depended on which webhook won a race.** A
|
|
94
|
+
Checkout payment fires `checkout.session.completed` and
|
|
95
|
+
`payment_intent.succeeded` in no guaranteed order; whichever applies the
|
|
96
|
+
payment builds the event, and the other is short-circuited as already
|
|
97
|
+
applied. The ids rode only on the session path, so a listener pinning a card
|
|
98
|
+
to the person it belongs to got nothing whenever the intent event landed
|
|
99
|
+
first — and the card stayed on the payer, attached to nobody.
|
|
100
|
+
|
|
101
|
+
Both paths now carry `customerId` and `savedPaymentMethodId`, under the same
|
|
102
|
+
`off_session` rule and with no extra provider call. Adoption still happens
|
|
103
|
+
once, because it keys off `adoptSavedPaymentMethod`, which only the session
|
|
104
|
+
completion sets. Until now the fire-once guard was the *absence* of those ids
|
|
105
|
+
on the intent path, which is precisely what broke them. Reported against
|
|
106
|
+
`0.10.0-rc.1`.
|
|
107
|
+
|
|
108
|
+
### Docs
|
|
109
|
+
- New **Deciding which cards may become the default**, answering the question
|
|
110
|
+
**What "ownership" means** (v0.9.0) ends on: ownership means only that a card
|
|
111
|
+
is attached to that provider customer, and this is how you tell the package
|
|
112
|
+
which of those cards are not the payer's own.
|
|
113
|
+
- `payment-method-policy-gate.spec.ts` fails the build if a new default-writing
|
|
114
|
+
call site skips the gate — the guarantee that makes a hook better than a
|
|
115
|
+
per-path switch, and the reason it is worth having at all.
|
|
116
|
+
|
|
117
|
+
## [0.9.0] - 2026-09-29
|
|
118
|
+
|
|
119
|
+
> **Not published separately — released as part of `0.10.0`.** Verifying this
|
|
120
|
+
> release on its own would have verified code `0.10.0` replaces: the setup-card
|
|
121
|
+
> path documented below was rewired a day later so a policy could be consulted
|
|
122
|
+
> before anything is written. The two were verified together instead. Pin
|
|
123
|
+
> `0.10.0`; everything here is in it.
|
|
124
|
+
|
|
125
|
+
A card kept while paying is now usable, and **the caller decides whether it
|
|
126
|
+
becomes the card everything else gets charged**. That second half is the
|
|
127
|
+
release: the first draft of this work adopted automatically, which is wrong for
|
|
128
|
+
any customer who pays on behalf of others, and the reporting consumer caught it
|
|
129
|
+
on production before we shipped.
|
|
130
|
+
|
|
131
|
+
### Added
|
|
132
|
+
- **`savePaymentMethod` on `createPaymentSession`** — `{ providerCustomerId,
|
|
133
|
+
adoptAsDefault? }`. One object, so asking to keep a card without naming a
|
|
134
|
+
customer to attach it to cannot be expressed; that combination left a
|
|
135
|
+
consumer's card on a provider-invented customer nothing in their database
|
|
136
|
+
pointed at. For Stripe it becomes `customer` plus
|
|
137
|
+
`payment_intent_data.setup_future_usage: 'off_session'`.
|
|
138
|
+
|
|
139
|
+
`customer` and `setup_future_usage` remain **unreserved**, so passthrough code
|
|
140
|
+
written before this is unaffected. Only a contradiction is refused — a
|
|
141
|
+
different customer, or `on_session` alongside `savePaymentMethod` — on the
|
|
142
|
+
same precedent as `paymentIntentData.metadata.invoiceId`.
|
|
143
|
+
|
|
144
|
+
- **`adoptAsDefault` on `createSetupSession`** and
|
|
145
|
+
`CustomerService.createSetupSession(..., { adoptAsDefault })`, **defaulting to
|
|
146
|
+
`true`** — what that path has always done. Pass `false` when collecting a card
|
|
147
|
+
for somebody the customer pays on behalf of.
|
|
148
|
+
|
|
149
|
+
- **`adoptPaymentMethod` on `RecurringPaymentGateway`** (optional), for a card
|
|
150
|
+
whose id is already resolved. `adoptSetupPaymentMethod` now delegates to the
|
|
151
|
+
same private tail, so "default only when the customer has none" exists once.
|
|
152
|
+
|
|
153
|
+
- **`customerId` in `providerData`** on payment-mode Checkout completions, and
|
|
154
|
+
**`providerCustomerId` / `paymentMethodId` on `PaidInvoiceEvent`** — both
|
|
155
|
+
nullable and "when present". A listener receives the event, not the normalised
|
|
156
|
+
webhook, so these ids were previously unreachable without re-reading the
|
|
157
|
+
payment and calling the provider.
|
|
158
|
+
|
|
159
|
+
- **`STRIPE_ADOPT_AS_DEFAULT_METADATA_KEY`**, the metadata key carrying the
|
|
160
|
+
adoption decision from session creation to the webhook that acts on it.
|
|
161
|
+
|
|
162
|
+
### Changed
|
|
163
|
+
- **Adoption is opt-in for payments, and silence means no.** A card kept during
|
|
164
|
+
a payment becomes the default only when the session asked for it.
|
|
165
|
+
|
|
166
|
+
A customer who pays on behalf of others is one provider customer holding
|
|
167
|
+
several people's cards, and the default is what everyone *without* their own
|
|
168
|
+
card is charged. Adopting automatically makes one third party's card the payer
|
|
169
|
+
for all the rest, invisibly to the cardholder. A recruiting agency with 53
|
|
170
|
+
client companies hit exactly this: one client's card became the account
|
|
171
|
+
default and was on course to pay for the other 52. Nothing was charged.
|
|
172
|
+
|
|
173
|
+
- **Only an `off_session` mandate is adopted.** A card kept `on_session` is
|
|
174
|
+
attached and logged and never becomes the off-session default: that consent is
|
|
175
|
+
for charges the customer is present for, and spending it on a renewal produces
|
|
176
|
+
an authentication-required decline nobody is there to answer.
|
|
177
|
+
|
|
178
|
+
- **Adoption runs only for a settled payment**, after the payment is recorded,
|
|
179
|
+
and can never fail the webhook. A throw would answer 500 and the provider
|
|
180
|
+
would redeliver for about three days, re-running the payment handler each
|
|
181
|
+
time. Failures are reported through `webhook.errorReporter` instead of being
|
|
182
|
+
swallowed silently, as they were before.
|
|
183
|
+
|
|
184
|
+
### Fixed
|
|
185
|
+
- **The webhook never wrote `PaymentCustomer.defaultPaymentMethodId`.** Only
|
|
186
|
+
`PaymentMethodService` mirrored it, and the webhook called the gateway
|
|
187
|
+
directly — so a card saved through the *documented* setup flow set the
|
|
188
|
+
provider's default and left the mirrored column null, making this README's
|
|
189
|
+
promise that you can render the current card without a provider round trip
|
|
190
|
+
untrue for the main path. Both adoption paths now go through
|
|
191
|
+
`PaymentMethodService` and mirror what they set. Found while verifying a
|
|
192
|
+
consumer's report, not reported.
|
|
193
|
+
|
|
194
|
+
### Docs
|
|
195
|
+
- New **Saving a card while paying**, **Choosing whether a saved card becomes
|
|
196
|
+
the default**, and **What "ownership" means** sections. The last one says
|
|
197
|
+
plainly that ownership means "attached to this provider customer" and nothing
|
|
198
|
+
more, which is weaker than the word suggests and matters to anyone building a
|
|
199
|
+
card picker on `listPaymentMethods`.
|
|
200
|
+
|
|
31
201
|
## [0.8.0] - 2026-09-28
|
|
32
202
|
|
|
33
203
|
Off-session charges can finally say **which brand is charging**. A consumer
|
package/README.md
CHANGED
|
@@ -24,6 +24,10 @@ Production-grade payments module for NestJS with GraphQL support. Invoicing (inc
|
|
|
24
24
|
- [Pinning a schedule to one card](#pinning-a-schedule-to-one-card)
|
|
25
25
|
- [Per-charge gateway options for renewals](#per-charge-gateway-options-for-renewals)
|
|
26
26
|
- [Saved Cards](#saved-cards)
|
|
27
|
+
- [What "ownership" means](#what-ownership-means)
|
|
28
|
+
- [Deciding which cards may become the default](#deciding-which-cards-may-become-the-default)
|
|
29
|
+
- [Saving a card while paying](#saving-a-card-while-paying)
|
|
30
|
+
- [Choosing whether a saved card becomes the default](#choosing-whether-a-saved-card-becomes-the-default)
|
|
27
31
|
- [Managing saved cards](#managing-saved-cards)
|
|
28
32
|
- [Stripe-specific off-session options](#stripe-specific-off-session-options-gatewayoptions)
|
|
29
33
|
- [E-Transfer](#e-transfer)
|
|
@@ -1062,7 +1066,136 @@ const { setupUrl, providerSetupId } = await customerService.createSetupSession(
|
|
|
1062
1066
|
|
|
1063
1067
|
**Completing the setup is a webhook, not a redirect.** Your success URL fires in the customer's browser; the card is not usable until `checkout.session.completed` arrives and the package records it. Do not tell the client their card is saved based on the redirect alone.
|
|
1064
1068
|
|
|
1065
|
-
**Which card gets charged.** The provider is the source of truth — for Stripe, `invoice_settings.default_payment_method`. `PaymentCustomer.defaultPaymentMethodId` mirrors it so you can render the current card without a provider round trip, but the package never consults that column to decide what to charge. One authority is what stops the two disagreeing.
|
|
1069
|
+
**Which card gets charged.** The provider is the source of truth — for Stripe, `invoice_settings.default_payment_method`. `PaymentCustomer.defaultPaymentMethodId` mirrors it so you can render the current card without a provider round trip, but the package never consults that column to decide what to charge. One authority is what stops the two disagreeing. A collected card becomes the default **only when the customer has none**, so a client adding a second card does not silently change what gets billed.
|
|
1070
|
+
|
|
1071
|
+
> **The mirror was not written on the webhook path before v0.9.0.** Only `PaymentMethodService` ever updated `defaultPaymentMethodId`, and the webhook called the gateway directly — so a card saved through the ordinary setup flow set the provider's default and left the column null. If you have been rendering from that column, it has been blank for cards saved that way; it is maintained from v0.9.0 on, and a `confirmSetup` call or any card-management write refreshes it for existing rows.
|
|
1072
|
+
|
|
1073
|
+
#### What "ownership" means
|
|
1074
|
+
|
|
1075
|
+
`ownsPaymentMethod`, `assertOwned` and every method taking a `paymentMethodId` mean one thing by ownership: **the card is attached to that client's provider customer.** Nothing more.
|
|
1076
|
+
|
|
1077
|
+
That is worth stating plainly, because a provider customer can end up holding cards that are not the payer's own. Anything paid with `savePaymentMethod` attaches, and so does any card saved through a setup session — so a customer who pays on behalf of others accumulates their cards in one wallet, and the package cannot tell them apart.
|
|
1078
|
+
|
|
1079
|
+
The consequences are yours to manage:
|
|
1080
|
+
|
|
1081
|
+
- `listPaymentMethods` returns **every** attached card, so a picker built straight onto it will offer a third party's card for somebody else's purchase.
|
|
1082
|
+
- `setDefaultPaymentMethod` accepts any attached card, because by this definition the client owns it.
|
|
1083
|
+
|
|
1084
|
+
What the package guarantees is narrower and worth having: it never *chooses* a card. Adoption is opt-in per session (below), an existing default is never overridden, and `createOffSessionPayment` refuses rather than falling back to whatever is on file. The package still does not know *which* cards are not the payer's own — but as of v0.10.0 you can tell it, per decision, with a policy: see [Deciding which cards may become the default](#deciding-which-cards-may-become-the-default).
|
|
1085
|
+
|
|
1086
|
+
|
|
1087
|
+
|
|
1088
|
+
#### Deciding which cards may become the default
|
|
1089
|
+
|
|
1090
|
+
*Added in v0.10.0.*
|
|
1091
|
+
|
|
1092
|
+
The section above ends by saying the package cannot tell a third party's card from the payer's own. This is how you tell it.
|
|
1093
|
+
|
|
1094
|
+
```typescript
|
|
1095
|
+
import type { IPaymentMethodPolicy } from '@ambushsoftworks/nestjs-payments-graphql';
|
|
1096
|
+
|
|
1097
|
+
class WalletPolicy implements IPaymentMethodPolicy {
|
|
1098
|
+
constructor(private readonly cards: CardOwnershipService) {}
|
|
1099
|
+
|
|
1100
|
+
async mayBecomeDefault({ clientDetailsId, paymentMethodId, trigger }) {
|
|
1101
|
+
// An account that pays for other people never gets an automatic default.
|
|
1102
|
+
if (trigger !== 'explicit') {
|
|
1103
|
+
return !(await this.cards.paysOnBehalfOfOthers(clientDetailsId));
|
|
1104
|
+
}
|
|
1105
|
+
// A deliberate click is allowed, unless the card is somebody else's.
|
|
1106
|
+
return !(await this.cards.belongsToThirdParty(paymentMethodId));
|
|
1107
|
+
}
|
|
1108
|
+
}
|
|
1109
|
+
|
|
1110
|
+
// In forRootAsync's useFactory:
|
|
1111
|
+
paymentMethodPolicyInstance: new WalletPolicy(cards),
|
|
1112
|
+
```
|
|
1113
|
+
|
|
1114
|
+
**It is asked at the only two places the package writes a default**, before the provider is called — so a refusal costs no network request. `trigger` says what caused the write:
|
|
1115
|
+
|
|
1116
|
+
| Trigger | Cause | A refusal |
|
|
1117
|
+
|---|---|---|
|
|
1118
|
+
| `explicit` | `setDefaultPaymentMethod` — a person chose this card | **throws** `PaymentMethodDefaultRefusedException` |
|
|
1119
|
+
| `setup` | a setup session completed, via the webhook or `confirmSetup` | logs, leaves the default alone |
|
|
1120
|
+
| `payment` | a buyer kept a card while paying and the session asked to adopt it | logs, leaves the default alone |
|
|
1121
|
+
|
|
1122
|
+
The explicit path throws because somebody is waiting for an answer, and a silent no-op reads as success while the card quietly does not stick. The automatic paths do not, because they run inside a webhook: throwing there answers 500 and Stripe redelivers for about three days.
|
|
1123
|
+
|
|
1124
|
+
In every case the card stays attached and chargeable by id. Only the default is untouched.
|
|
1125
|
+
|
|
1126
|
+
**A policy that fails is a refusal.** If `mayBecomeDefault` throws, rejects, or takes longer than `paymentMethodPolicyTimeoutMs` (default 5000), the package logs at error level and treats the answer as `false`.
|
|
1127
|
+
|
|
1128
|
+
> This is the opposite of every other consumer hook here, and deliberately so. A branding resolver that fails lets the payment go through, because the cost of proceeding is a missing brand name on a statement. The cost of proceeding *here* is charging the wrong person's card; the cost of refusing is a customer with no default and a loud `no_payment_method` on their next renewal. **Fail open for cosmetic, fail closed for authority.** It is also the only timeout in the package — everywhere else a slow callback is left to finish, because there the safe answer is "proceed" and a timeout could only cause harm.
|
|
1129
|
+
|
|
1130
|
+
**Why a hook and not a flag on each path.** [Saving a card while paying](#saving-a-card-while-paying) already lets a session say whether its card should be adopted, and that stays — it is the first line, and it is the right place to answer when you know at checkout time. The policy is the backstop: it guards the *two functions* rather than the paths anyone thought to list, so a path added in a later version inherits it. A spec fails the build if a new default-writing call site skips the gate.
|
|
1131
|
+
|
|
1132
|
+
Use both. A session flag cannot help when the decision depends on something you only learn from the webhook that triggers the adoption.
|
|
1133
|
+
|
|
1134
|
+
> **This governs the default, and only the default.** A policy decides what
|
|
1135
|
+
> gets charged when nothing else is specified — it does not decide which cards
|
|
1136
|
+
> Stripe's **hosted Checkout page offers back to the payer**. That page selects
|
|
1137
|
+
> saved cards by the provider's own `allow_redisplay` marking, not by the
|
|
1138
|
+
> default, and a card collected through a setup-mode Checkout session is marked
|
|
1139
|
+
> redisplayable. So a customer who holds a third party's card can still be shown
|
|
1140
|
+
> it, pre-selected, on a hosted page for a different purchase. The package does
|
|
1141
|
+
> not yet control that marking; if it matters to you, keep the payer's saved
|
|
1142
|
+
> cards off sessions where they should not appear, and say so — we have an open
|
|
1143
|
+
> design request for it.
|
|
1144
|
+
|
|
1145
|
+
**Your gateway needs `resolveSetupPaymentMethod`** if you wrote your own and you register a policy. A setup session only reveals its card when it completes, so the package has to be able to ask what was collected before anything is written. Without it a setup-session adoption is **refused** rather than performed ungated, and the refusal is logged with what to implement. The bundled Stripe gateway has it.
|
|
1146
|
+
|
|
1147
|
+
#### Saving a card while paying
|
|
1148
|
+
|
|
1149
|
+
*Added in v0.9.0.*
|
|
1150
|
+
|
|
1151
|
+
A buyer paying through hosted Checkout can keep the card for later. Pass `savePaymentMethod` and the gateway attaches it to the customer you name:
|
|
1152
|
+
|
|
1153
|
+
```typescript
|
|
1154
|
+
const customer = await customerService.createOrLinkCustomer(
|
|
1155
|
+
divisionId, clientDetailsId, email, name,
|
|
1156
|
+
);
|
|
1157
|
+
|
|
1158
|
+
await gateway.createPaymentSession({
|
|
1159
|
+
invoiceId, amount, currency: 'CAD', successUrl, cancelUrl,
|
|
1160
|
+
savePaymentMethod: {
|
|
1161
|
+
providerCustomerId: customer.providerCustomerId,
|
|
1162
|
+
adoptAsDefault: true, // see below — defaults to false
|
|
1163
|
+
},
|
|
1164
|
+
});
|
|
1165
|
+
```
|
|
1166
|
+
|
|
1167
|
+
**One object, not a flag and a customer id.** A card needs a customer to attach to; without one the provider either refuses or invents a customer nothing in your database points at, and the card is lost with no error anywhere. A consumer hit exactly that and spent a day on it. This shape makes it unrepresentable.
|
|
1168
|
+
|
|
1169
|
+
**`adoptAsDefault` decides whether it becomes the card auto-renew uses, and it defaults to `false`.**
|
|
1170
|
+
|
|
1171
|
+
| `adoptAsDefault` | customer has no default | customer has a default |
|
|
1172
|
+
|---|---|---|
|
|
1173
|
+
| `true` | the saved card becomes the default | unchanged — never overridden |
|
|
1174
|
+
| `false` or absent | unchanged: still no default | unchanged |
|
|
1175
|
+
|
|
1176
|
+
The card is attached in every row, and chargeable by id via `createOffSessionPayment`'s `paymentMethodId`. Only the default differs.
|
|
1177
|
+
|
|
1178
|
+
> **Why silence means no.** Keeping a card and choosing what everything *else* gets charged are different decisions, and only you know whether the payer owns the card they just typed. A customer who pays on behalf of others — an agency, a parent company, a bookkeeper — is one provider customer holding several people's cards, and the default is what everyone *without* their own card is charged. A consumer hit this on production: a recruiting agency with 53 client companies paid for one client's posting, that client's card became the account default, and it was on course to pay for the other 52. Nothing was charged; they caught it within the hour. Pass `true` when the payer is buying for themselves, and leave it out when they are not.
|
|
1179
|
+
|
|
1180
|
+
The card id reaches your listener on `PaidInvoiceEvent` as `paymentMethodId`, with `providerCustomerId` beside it, so you can file it against whoever it belongs to without a provider round trip.
|
|
1181
|
+
|
|
1182
|
+
**Only `off_session` counts.** The package adopts a card only when the mandate says the customer will not be present for future charges. A card kept `on_session` is attached and logged, and never becomes the off-session default — that consent is for charges the customer is there for, and using it for a renewal is how you get an authentication-required decline at 02:00.
|
|
1183
|
+
|
|
1184
|
+
**Equivalent through `gatewayOptions`.** `customer` and `setup_future_usage` are not reserved, so code written before v0.9.0 keeps working. What is refused is a *contradiction* — naming a different customer, or asking for `on_session` alongside `savePaymentMethod` — because resolving it either way would be a guess. The package writes its decision into the PaymentIntent's metadata under `adoptAsDefault` (exported as `STRIPE_ADOPT_AS_DEFAULT_METADATA_KEY`); keep that key out of your own metadata.
|
|
1185
|
+
|
|
1186
|
+
#### Choosing whether a saved card becomes the default
|
|
1187
|
+
|
|
1188
|
+
The same choice applies to setup sessions, where it **defaults to `true`** — that is what `createSetupSession` has always done, so nothing changes for existing consumers:
|
|
1189
|
+
|
|
1190
|
+
```typescript
|
|
1191
|
+
await customerService.createSetupSession(
|
|
1192
|
+
divisionId, clientDetailsId, email, name, successUrl, cancelUrl,
|
|
1193
|
+
'stripe', undefined,
|
|
1194
|
+
{ adoptAsDefault: false }, // a card you are collecting for a third party
|
|
1195
|
+
);
|
|
1196
|
+
```
|
|
1197
|
+
|
|
1198
|
+
**The answer travels with the session.** Adoption happens in two places that cannot see each other — the `checkout.session.completed` webhook, and `confirmSetup` when you confirm on the redirect — and either may land first. The package writes the decision onto the SetupIntent at session creation, so both read the same answer and `confirmSetup` needs no extra argument. Clearing a default yourself afterwards does not work: the webhook can land after your clear and set it again.
|
|
1066
1199
|
|
|
1067
1200
|
#### Managing saved cards
|
|
1068
1201
|
|
|
@@ -1137,7 +1270,7 @@ Declare `@@unique([clientDetailsId, provider])` on `PaymentCustomer` to match, a
|
|
|
1137
1270
|
|
|
1138
1271
|
**Keep the `divisionId` column** even though you no longer key on it. The bundled adapter still writes it on create and `assertPaymentsSchema` still requires it, so dropping it because "we do not scope by division" fails at boot — or on the first card save if you skip the schema assertion. It stays useful as a record of which division first created the customer.
|
|
1139
1272
|
|
|
1140
|
-
A custom gateway must implement `adoptSetupPaymentMethod` to support saved cards.
|
|
1273
|
+
A custom gateway must implement `adoptSetupPaymentMethod` to support saved cards, `adoptPaymentMethod` (v0.9.0) to support a card kept while paying, and `resolveSetupPaymentMethod` (v0.10.0) so a setup session's card can be reported before it is adopted — the second takes a payment method id the gateway has already resolved, rather than a setup intent. Both are optional on `RecurringPaymentGateway` so older gateways still compile; without them the webhook logs a warning and the card stays unusable for auto-charge. Implement them over one shared code path: "default only when the customer has none, never override" stops being true the moment there are two copies of it.
|
|
1141
1274
|
|
|
1142
1275
|
#### Stripe-specific off-session options (`gatewayOptions`)
|
|
1143
1276
|
|
|
@@ -1250,6 +1383,8 @@ paymentEventListenerInstance: {
|
|
|
1250
1383
|
|
|
1251
1384
|
Event payloads include `metadata` from the invoice plus the full `lineItems` snapshot so consumers don't need to re-query.
|
|
1252
1385
|
|
|
1386
|
+
`PaidInvoiceEvent` also carries `providerCustomerId` and `paymentMethodId` (v0.9.0), so a listener can act on a card the payment kept — filing it against the party it belongs to, for instance — without re-reading the payment and calling the provider. Both are **nullable and "when present", not a guarantee**: five paths fire `onInvoicePaid`, and a manually recorded payment or an e-transfer confirmation has no card at all. The recurring auto-charge path knows the card but does not yet pass it here.
|
|
1387
|
+
|
|
1253
1388
|
### Transactions
|
|
1254
1389
|
|
|
1255
1390
|
`ITransactionManager.runInTransaction(fn)` gives the callback a `TransactionRepositories` bag with all repositories bound to the same ORM transaction. The package uses this internally when recording payments and mutating totals.
|
|
@@ -1523,6 +1658,8 @@ Two database constraints are load-bearing wherever you are coming from, because
|
|
|
1523
1658
|
|--------|------|-------------|
|
|
1524
1659
|
| `paymentEventListenerInstance` | `IPaymentEventListener` | Domain callbacks — see [Event Listeners](#event-listeners). Only `onInvoicePaid` is required. |
|
|
1525
1660
|
| `recurringChargeOptionsResolverInstance` | `IRecurringChargeOptionsResolver` | Supplies `gatewayOptions` for each renewal charge — see [Per-charge gateway options for renewals](#per-charge-gateway-options-for-renewals). Absent, renewals charge as before; a resolver that throws degrades to charging without options rather than failing the renewal. |
|
|
1661
|
+
| `paymentMethodPolicyInstance` | `IPaymentMethodPolicy` | Decides whether a card may become a customer's default — see [Deciding which cards may become the default](#deciding-which-cards-may-become-the-default). Absent, every path behaves as before. A policy that throws, rejects or times out is treated as a **refusal**. |
|
|
1662
|
+
| `paymentMethodPolicyTimeoutMs` | `number` | How long `mayBecomeDefault` may take before the answer is treated as a refusal. Default `5000`. The only timeout in the package. |
|
|
1526
1663
|
| `webhook` | `{ errorReporter? }` | `webhook.errorReporter` is called with anything a webhook handler throws. Route it somewhere you will see it: since v0.5.0 a failed handler answers 500 and Stripe retries for ~3 days. |
|
|
1527
1664
|
| `tax` | `{ requireExplicit? }` | See [Tax](#tax). Set `tax.requireExplicit` if you bill in more than one jurisdiction. |
|
|
1528
1665
|
| `features` | see below | Feature flags. Enabling one without its backing repository throws at boot. |
|
|
@@ -1600,6 +1737,8 @@ import {
|
|
|
1600
1737
|
TRANSACTION_MANAGER,
|
|
1601
1738
|
PAYMENT_EVENT_LISTENER,
|
|
1602
1739
|
RECURRING_CHARGE_OPTIONS_RESOLVER,
|
|
1740
|
+
PAYMENT_METHOD_POLICY,
|
|
1741
|
+
PAYMENT_METHOD_POLICY_TIMEOUT,
|
|
1603
1742
|
PAYMENT_EMAIL_SENDER,
|
|
1604
1743
|
PAYMENT_CLIENT_RESOLVER,
|
|
1605
1744
|
PAYMENT_EMAIL_BRANDING_RESOLVER,
|
|
@@ -1648,6 +1787,7 @@ All exceptions extend `PaymentException` (a plain `Error` subclass) with a stabl
|
|
|
1648
1787
|
| `PaymentIntentOperationNotSupportedException` | `PAYMENT_INTENT_OPERATION_NOT_SUPPORTED` |
|
|
1649
1788
|
| `InvalidTaxConfigurationException` | `INVALID_TAX_CONFIGURATION` |
|
|
1650
1789
|
| `PaymentMethodNotOwnedException` | `PAYMENT_METHOD_NOT_OWNED` |
|
|
1790
|
+
| `PaymentMethodDefaultRefusedException` | `PAYMENT_METHOD_DEFAULT_REFUSED` |
|
|
1651
1791
|
| `PaymentMethodOperationNotSupportedException` | `PAYMENT_METHOD_OPERATION_NOT_SUPPORTED` |
|
|
1652
1792
|
| `NoChargeablePaymentMethodException` | `NO_CHARGEABLE_PAYMENT_METHOD` |
|
|
1653
1793
|
|
package/dist/constants.d.ts
CHANGED
|
@@ -9,6 +9,8 @@ export declare const PAYMENT_EVENT_LISTENER: unique symbol;
|
|
|
9
9
|
export declare const RECURRING_INVOICE_REPOSITORY: unique symbol;
|
|
10
10
|
export declare const PAYMENT_CUSTOMER_REPOSITORY: unique symbol;
|
|
11
11
|
export declare const RECURRING_CHARGE_OPTIONS_RESOLVER: unique symbol;
|
|
12
|
+
export declare const PAYMENT_METHOD_POLICY: unique symbol;
|
|
13
|
+
export declare const PAYMENT_METHOD_POLICY_TIMEOUT: unique symbol;
|
|
12
14
|
export declare const PAYMENT_EMAIL_SENDER: unique symbol;
|
|
13
15
|
export declare const PAYMENT_CLIENT_RESOLVER: unique symbol;
|
|
14
16
|
export declare const PAYMENT_EMAIL_BRANDING_RESOLVER: unique symbol;
|
package/dist/constants.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.PAYMENT_SERVICE = exports.TAX_OPTIONS = exports.E_TRANSFER_CODE_PREFIX = exports.DEFAULT_CURRENCY = exports.WEBHOOK_ERROR_REPORTER = exports.PAYMENT_EMAIL_TEMPLATE_RENDERER = exports.PAYMENT_EMAIL_BRANDING_RESOLVER = exports.PAYMENT_CLIENT_RESOLVER = exports.PAYMENT_EMAIL_SENDER = exports.RECURRING_CHARGE_OPTIONS_RESOLVER = exports.PAYMENT_CUSTOMER_REPOSITORY = exports.RECURRING_INVOICE_REPOSITORY = exports.PAYMENT_EVENT_LISTENER = exports.TRANSACTION_MANAGER = exports.WEBHOOK_IDEMPOTENCY_REPOSITORY = exports.PAYMENT_CONFIG_REPOSITORY = exports.PAYMENT_PLAN_REPOSITORY = exports.REFUND_REPOSITORY = exports.PAYMENT_REPOSITORY = exports.INVOICE_REPOSITORY = void 0;
|
|
3
|
+
exports.PAYMENT_SERVICE = exports.TAX_OPTIONS = exports.E_TRANSFER_CODE_PREFIX = exports.DEFAULT_CURRENCY = exports.WEBHOOK_ERROR_REPORTER = exports.PAYMENT_EMAIL_TEMPLATE_RENDERER = exports.PAYMENT_EMAIL_BRANDING_RESOLVER = exports.PAYMENT_CLIENT_RESOLVER = exports.PAYMENT_EMAIL_SENDER = exports.PAYMENT_METHOD_POLICY_TIMEOUT = exports.PAYMENT_METHOD_POLICY = exports.RECURRING_CHARGE_OPTIONS_RESOLVER = exports.PAYMENT_CUSTOMER_REPOSITORY = exports.RECURRING_INVOICE_REPOSITORY = exports.PAYMENT_EVENT_LISTENER = exports.TRANSACTION_MANAGER = exports.WEBHOOK_IDEMPOTENCY_REPOSITORY = exports.PAYMENT_CONFIG_REPOSITORY = exports.PAYMENT_PLAN_REPOSITORY = exports.REFUND_REPOSITORY = exports.PAYMENT_REPOSITORY = exports.INVOICE_REPOSITORY = void 0;
|
|
4
4
|
exports.INVOICE_REPOSITORY = Symbol('IInvoiceRepository');
|
|
5
5
|
exports.PAYMENT_REPOSITORY = Symbol('IPaymentRepository');
|
|
6
6
|
exports.REFUND_REPOSITORY = Symbol('IRefundRepository');
|
|
@@ -12,6 +12,8 @@ exports.PAYMENT_EVENT_LISTENER = Symbol('IPaymentEventListener');
|
|
|
12
12
|
exports.RECURRING_INVOICE_REPOSITORY = Symbol('IRecurringInvoiceRepository');
|
|
13
13
|
exports.PAYMENT_CUSTOMER_REPOSITORY = Symbol('IPaymentCustomerRepository');
|
|
14
14
|
exports.RECURRING_CHARGE_OPTIONS_RESOLVER = Symbol('IRecurringChargeOptionsResolver');
|
|
15
|
+
exports.PAYMENT_METHOD_POLICY = Symbol('IPaymentMethodPolicy');
|
|
16
|
+
exports.PAYMENT_METHOD_POLICY_TIMEOUT = Symbol('PaymentMethodPolicyTimeout');
|
|
15
17
|
exports.PAYMENT_EMAIL_SENDER = Symbol('IPaymentEmailSender');
|
|
16
18
|
exports.PAYMENT_CLIENT_RESOLVER = Symbol('IPaymentClientResolver');
|
|
17
19
|
exports.PAYMENT_EMAIL_BRANDING_RESOLVER = Symbol('IPaymentEmailBrandingResolver');
|
package/dist/constants.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"constants.js","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":";;;AAAa,QAAA,kBAAkB,GAAG,MAAM,CAAC,oBAAoB,CAAC,CAAC;AAClD,QAAA,kBAAkB,GAAG,MAAM,CAAC,oBAAoB,CAAC,CAAC;AAClD,QAAA,iBAAiB,GAAG,MAAM,CAAC,mBAAmB,CAAC,CAAC;AAChD,QAAA,uBAAuB,GAAG,MAAM,CAAC,wBAAwB,CAAC,CAAC;AAC3D,QAAA,yBAAyB,GAAG,MAAM,CAAC,0BAA0B,CAAC,CAAC;AAC/D,QAAA,8BAA8B,GAAG,MAAM,CAClD,+BAA+B,CAChC,CAAC;AACW,QAAA,mBAAmB,GAAG,MAAM,CAAC,qBAAqB,CAAC,CAAC;AACpD,QAAA,sBAAsB,GAAG,MAAM,CAAC,uBAAuB,CAAC,CAAC;AACzD,QAAA,4BAA4B,GAAG,MAAM,CAChD,6BAA6B,CAC9B,CAAC;AACW,QAAA,2BAA2B,GAAG,MAAM,CAAC,4BAA4B,CAAC,CAAC;AAOnE,QAAA,iCAAiC,GAAG,MAAM,CACrD,iCAAiC,CAClC,CAAC;
|
|
1
|
+
{"version":3,"file":"constants.js","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":";;;AAAa,QAAA,kBAAkB,GAAG,MAAM,CAAC,oBAAoB,CAAC,CAAC;AAClD,QAAA,kBAAkB,GAAG,MAAM,CAAC,oBAAoB,CAAC,CAAC;AAClD,QAAA,iBAAiB,GAAG,MAAM,CAAC,mBAAmB,CAAC,CAAC;AAChD,QAAA,uBAAuB,GAAG,MAAM,CAAC,wBAAwB,CAAC,CAAC;AAC3D,QAAA,yBAAyB,GAAG,MAAM,CAAC,0BAA0B,CAAC,CAAC;AAC/D,QAAA,8BAA8B,GAAG,MAAM,CAClD,+BAA+B,CAChC,CAAC;AACW,QAAA,mBAAmB,GAAG,MAAM,CAAC,qBAAqB,CAAC,CAAC;AACpD,QAAA,sBAAsB,GAAG,MAAM,CAAC,uBAAuB,CAAC,CAAC;AACzD,QAAA,4BAA4B,GAAG,MAAM,CAChD,6BAA6B,CAC9B,CAAC;AACW,QAAA,2BAA2B,GAAG,MAAM,CAAC,4BAA4B,CAAC,CAAC;AAOnE,QAAA,iCAAiC,GAAG,MAAM,CACrD,iCAAiC,CAClC,CAAC;AASW,QAAA,qBAAqB,GAAG,MAAM,CAAC,sBAAsB,CAAC,CAAC;AAOvD,QAAA,6BAA6B,GAAG,MAAM,CAAC,4BAA4B,CAAC,CAAC;AAGrE,QAAA,oBAAoB,GAAG,MAAM,CAAC,qBAAqB,CAAC,CAAC;AACrD,QAAA,uBAAuB,GAAG,MAAM,CAAC,wBAAwB,CAAC,CAAC;AAC3D,QAAA,+BAA+B,GAAG,MAAM,CACnD,+BAA+B,CAChC,CAAC;AACW,QAAA,+BAA+B,GAAG,MAAM,CACnD,+BAA+B,CAChC,CAAC;AAGW,QAAA,sBAAsB,GAAG,MAAM,CAAC,sBAAsB,CAAC,CAAC;AAGxD,QAAA,gBAAgB,GAAG,MAAM,CAAC,iBAAiB,CAAC,CAAC;AAC7C,QAAA,sBAAsB,GAAG,MAAM,CAAC,qBAAqB,CAAC,CAAC;AAGvD,QAAA,WAAW,GAAG,MAAM,CAAC,YAAY,CAAC,CAAC;AAYnC,QAAA,eAAe,GAAG,MAAM,CAAC,gBAAgB,CAAC,CAAC"}
|
|
@@ -50,6 +50,9 @@ export declare class InvalidTaxConfigurationException extends PaymentException {
|
|
|
50
50
|
export declare class PaymentMethodNotOwnedException extends PaymentException {
|
|
51
51
|
constructor(message: string);
|
|
52
52
|
}
|
|
53
|
+
export declare class PaymentMethodDefaultRefusedException extends PaymentException {
|
|
54
|
+
constructor(message: string);
|
|
55
|
+
}
|
|
53
56
|
export declare class PaymentMethodOperationNotSupportedException extends PaymentException {
|
|
54
57
|
constructor(message: string);
|
|
55
58
|
}
|
package/dist/exceptions/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.NoChargeablePaymentMethodException = exports.PaymentMethodOperationNotSupportedException = exports.PaymentMethodNotOwnedException = exports.InvalidTaxConfigurationException = exports.PaymentIntentOperationNotSupportedException = exports.PaymentIntentNotCancellableException = exports.PaymentIntentNotReusableException = exports.PublishableKeyNotConfiguredException = exports.UniqueConstraintViolationException = exports.InvalidRecurringInvoiceStateException = exports.InvalidPaymentStateException = exports.RefundNotAllowedException = exports.InvoiceNumberExhaustedException = exports.InvoiceNotPayableException = exports.DuplicatePaymentException = exports.PaymentGatewayException = exports.PaymentAmountExceededException = exports.InvalidInvoiceStateException = exports.PaymentException = void 0;
|
|
3
|
+
exports.NoChargeablePaymentMethodException = exports.PaymentMethodOperationNotSupportedException = exports.PaymentMethodDefaultRefusedException = exports.PaymentMethodNotOwnedException = exports.InvalidTaxConfigurationException = exports.PaymentIntentOperationNotSupportedException = exports.PaymentIntentNotCancellableException = exports.PaymentIntentNotReusableException = exports.PublishableKeyNotConfiguredException = exports.UniqueConstraintViolationException = exports.InvalidRecurringInvoiceStateException = exports.InvalidPaymentStateException = exports.RefundNotAllowedException = exports.InvoiceNumberExhaustedException = exports.InvoiceNotPayableException = exports.DuplicatePaymentException = exports.PaymentGatewayException = exports.PaymentAmountExceededException = exports.InvalidInvoiceStateException = exports.PaymentException = void 0;
|
|
4
4
|
class PaymentException extends Error {
|
|
5
5
|
constructor(message, code) {
|
|
6
6
|
super(message);
|
|
@@ -108,6 +108,12 @@ class PaymentMethodNotOwnedException extends PaymentException {
|
|
|
108
108
|
}
|
|
109
109
|
}
|
|
110
110
|
exports.PaymentMethodNotOwnedException = PaymentMethodNotOwnedException;
|
|
111
|
+
class PaymentMethodDefaultRefusedException extends PaymentException {
|
|
112
|
+
constructor(message) {
|
|
113
|
+
super(message, 'PAYMENT_METHOD_DEFAULT_REFUSED');
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
exports.PaymentMethodDefaultRefusedException = PaymentMethodDefaultRefusedException;
|
|
111
117
|
class PaymentMethodOperationNotSupportedException extends PaymentException {
|
|
112
118
|
constructor(message) {
|
|
113
119
|
super(message, 'PAYMENT_METHOD_OPERATION_NOT_SUPPORTED');
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/exceptions/index.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/exceptions/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -11,6 +11,10 @@ export interface PaymentGateway {
|
|
|
11
11
|
metadata?: Record<string, string>;
|
|
12
12
|
expiresAt?: Date;
|
|
13
13
|
gatewayOptions?: Record<string, unknown>;
|
|
14
|
+
savePaymentMethod?: {
|
|
15
|
+
providerCustomerId: string;
|
|
16
|
+
adoptAsDefault?: boolean;
|
|
17
|
+
};
|
|
14
18
|
idempotencyKey?: string;
|
|
15
19
|
}): Promise<{
|
|
16
20
|
checkoutUrl: string;
|
|
@@ -30,6 +30,7 @@ export interface RecurringPaymentGateway {
|
|
|
30
30
|
cancelUrl: string;
|
|
31
31
|
metadata?: Record<string, string>;
|
|
32
32
|
currency?: string;
|
|
33
|
+
adoptAsDefault?: boolean;
|
|
33
34
|
}): Promise<{
|
|
34
35
|
setupUrl: string;
|
|
35
36
|
providerSetupId: string;
|
|
@@ -41,6 +42,19 @@ export interface RecurringPaymentGateway {
|
|
|
41
42
|
paymentMethodId: string | null;
|
|
42
43
|
madeDefault: boolean;
|
|
43
44
|
}>;
|
|
45
|
+
resolveSetupPaymentMethod?(params: {
|
|
46
|
+
setupIntentId: string;
|
|
47
|
+
}): Promise<{
|
|
48
|
+
paymentMethodId: string | null;
|
|
49
|
+
adoptAsDefault: boolean;
|
|
50
|
+
}>;
|
|
51
|
+
adoptPaymentMethod?(params: {
|
|
52
|
+
providerCustomerId: string;
|
|
53
|
+
paymentMethodId: string;
|
|
54
|
+
}): Promise<{
|
|
55
|
+
paymentMethodId: string | null;
|
|
56
|
+
madeDefault: boolean;
|
|
57
|
+
}>;
|
|
44
58
|
listPaymentMethods?(params: {
|
|
45
59
|
providerCustomerId: string;
|
|
46
60
|
}): Promise<SavedPaymentMethod[]>;
|
|
@@ -5,7 +5,7 @@ import type { Request } from 'express';
|
|
|
5
5
|
import { GatewayRegistryService } from '../gateway-registry.service';
|
|
6
6
|
import { PaymentService } from '../../services/payment.service';
|
|
7
7
|
import { RefundService } from '../../services/refund.service';
|
|
8
|
-
import {
|
|
8
|
+
import { PaymentMethodService } from '../../services/payment-method.service';
|
|
9
9
|
export declare const PAYMENTS_WEBHOOK = "payments_webhook";
|
|
10
10
|
export declare const PaymentsWebhook: () => <TFunction extends Function, Y>(target: TFunction | object, propertyKey?: string | symbol, descriptor?: TypedPropertyDescriptor<Y>) => void;
|
|
11
11
|
export declare class StripeWebhookController implements OnModuleInit {
|
|
@@ -13,15 +13,17 @@ export declare class StripeWebhookController implements OnModuleInit {
|
|
|
13
13
|
private readonly gatewayRegistry;
|
|
14
14
|
private readonly paymentService;
|
|
15
15
|
private readonly refundService;
|
|
16
|
-
private readonly
|
|
16
|
+
private readonly paymentMethodService;
|
|
17
17
|
private readonly logger;
|
|
18
18
|
private errorReporter;
|
|
19
|
-
constructor(moduleRef: ModuleRef, gatewayRegistry: GatewayRegistryService, paymentService: PaymentService, refundService: RefundService,
|
|
19
|
+
constructor(moduleRef: ModuleRef, gatewayRegistry: GatewayRegistryService, paymentService: PaymentService, refundService: RefundService, paymentMethodService: PaymentMethodService);
|
|
20
20
|
onModuleInit(): void;
|
|
21
21
|
handleStripeWebhook(req: RawBodyRequest<Request>): Promise<{
|
|
22
22
|
received: boolean;
|
|
23
23
|
}>;
|
|
24
24
|
private routeEvent;
|
|
25
25
|
private adoptSetupPaymentMethod;
|
|
26
|
+
private adoptPaymentMethod;
|
|
27
|
+
private adopt;
|
|
26
28
|
private handleWithRetry;
|
|
27
29
|
}
|