@wtfalch/payments 0.1.0 → 0.2.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/dist/stripe.js CHANGED
@@ -160,6 +160,13 @@ export function createStripeProvider(options) {
160
160
  };
161
161
  },
162
162
  async createRecurringAgreement(input) {
163
+ // Vipps-only: a SetupIntent has no pricing type or cap to declare up
164
+ // front -- it only saves a payment method, and the amount for each
165
+ // later charge is decided at `chargeRecurringAgreement` time. See the
166
+ // `variablePricing` doc comment on `CreateRecurringAgreementInput`.
167
+ if (input.variablePricing) {
168
+ throw new PaymentProviderError('stripe', 'variablePricing is not supported: a Stripe SetupIntent has no pricing type or cap to declare up front');
169
+ }
163
170
  const si = await call('/setup_intents', {
164
171
  usage: 'off_session',
165
172
  'metadata[reference]': input.reference,
package/dist/types.d.ts CHANGED
@@ -64,7 +64,10 @@ export interface CreateRecurringAgreementInput {
64
64
  readonly reference: string;
65
65
  /** The periodic charge amount. Stripe's SetupIntent does not itself need
66
66
  * one, but it is required here for symmetry with Vipps and because a
67
- * caller building the agreement already knows it. */
67
+ * caller building the agreement already knows it. Under `variablePricing`,
68
+ * Vipps ignores this for the agreement itself (there is no fixed price to
69
+ * declare); it is still required, as an estimate for Stripe's metadata and
70
+ * for a caller that has not yet decided the actual per-charge amounts. */
68
71
  readonly amount: Money;
69
72
  readonly productName: string;
70
73
  /** Where the payer returns after approving the agreement. */
@@ -76,6 +79,21 @@ export interface CreateRecurringAgreementInput {
76
79
  readonly unit: 'day' | 'week' | 'month';
77
80
  readonly count: number;
78
81
  };
82
+ /** Vipps Recurring `pricing.type: 'VARIABLE'`: the agreement has no fixed
83
+ * price, only a `suggestedMaxAmount` shown to the payer when they approve
84
+ * it (the payer may accept a different max, and can change it later).
85
+ * Each `chargeRecurringAgreement` call then charges its own amount --
86
+ * e.g. a plan fee plus that month's metered usage -- as long as it is at
87
+ * or below the max the payer accepted; a charge above it is held `DUE`
88
+ * and fails if the payer has not raised their max by `due` + `retryDays`.
89
+ * Omit for the existing fixed-price ("LEGACY") behaviour, unchanged.
90
+ * Vipps-only: `createStripeProvider` throws `PaymentProviderError` if this
91
+ * is given, since a Stripe SetupIntent has no pricing type or cap to
92
+ * declare up front. Same currency as `amount`.
93
+ * https://developer.vippsmobilepay.com/api/recurring/ */
94
+ readonly variablePricing?: {
95
+ readonly suggestedMaxAmount: number;
96
+ };
79
97
  readonly idempotencyKey?: string;
80
98
  }
81
99
  export interface RecurringAgreementResult {
package/dist/vipps.js CHANGED
@@ -187,12 +187,21 @@ export function createVippsProvider(options) {
187
187
  };
188
188
  },
189
189
  async createRecurringAgreement(input) {
190
- const agreement = await post('/recurring/v3/agreements', {
191
- pricing: {
190
+ // pricing.type LEGACY (fixed amount) vs VARIABLE (suggestedMaxAmount,
191
+ // no fixed amount): https://developer.vippsmobilepay.com/api/recurring/
192
+ const pricing = input.variablePricing
193
+ ? {
194
+ type: 'VARIABLE',
195
+ currency: input.amount.currency.toUpperCase(),
196
+ suggestedMaxAmount: input.variablePricing.suggestedMaxAmount,
197
+ }
198
+ : {
192
199
  type: 'LEGACY',
193
200
  amount: input.amount.value,
194
201
  currency: input.amount.currency.toUpperCase(),
195
- },
202
+ };
203
+ const agreement = await post('/recurring/v3/agreements', {
204
+ pricing,
196
205
  interval: {
197
206
  unit: (input.interval?.unit ?? 'month').toUpperCase(),
198
207
  count: input.interval?.count ?? 1,
@@ -212,9 +221,16 @@ export function createVippsProvider(options) {
212
221
  };
213
222
  },
214
223
  async chargeRecurringAgreement(agreementReference, input) {
224
+ // No `currency` field here: the charge schema at
225
+ // https://developer.vippsmobilepay.com/api/recurring/ has no such
226
+ // property -- the charge always uses the agreement's own currency, so
227
+ // sending one was a mismatch with the documented request body (fixed
228
+ // here, not merely renamed). For a VARIABLE agreement, Vipps enforces
229
+ // amount <= the payer's accepted max itself: a charge above it is
230
+ // held `DUE`, not rejected synchronously here, so this adapter does
231
+ // not duplicate that check.
215
232
  const charge = await post(`/recurring/v3/agreements/${agreementReference}/charges`, {
216
233
  amount: input.amount.value,
217
- currency: input.amount.currency.toUpperCase(),
218
234
  transactionType: 'DIRECT_CAPTURE',
219
235
  due: input.dueDate ?? tomorrowIsoDate(),
220
236
  retryDays: 0,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wtfalch/payments",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "One PaymentProvider interface -- create a payment, capture, refund, set up and charge a recurring agreement, verify a webhook -- over Stripe and Vipps MobilePay. Holds no card data.",
5
5
  "repository": {
6
6
  "type": "git",