@antelopejs/interface-subscriptions 0.0.0-stage → 0.0.1
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/LICENSE +190 -0
- package/README.md +215 -2
- package/dist/antelope.test.d.ts +2 -0
- package/dist/antelope.test.js +10 -0
- package/dist/antelope.test.js.map +1 -0
- package/dist/errors.d.ts +68 -0
- package/dist/errors.js +85 -0
- package/dist/errors.js.map +1 -0
- package/dist/functions.d.ts +101 -0
- package/dist/functions.js +104 -0
- package/dist/functions.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +21 -0
- package/dist/index.js.map +1 -0
- package/dist/tests/subscriptions-changes.test.d.ts +1 -0
- package/dist/tests/subscriptions-changes.test.js +194 -0
- package/dist/tests/subscriptions-changes.test.js.map +1 -0
- package/dist/tests/subscriptions.test.d.ts +1 -0
- package/dist/tests/subscriptions.test.js +144 -0
- package/dist/tests/subscriptions.test.js.map +1 -0
- package/dist/tests/support.d.ts +77 -0
- package/dist/tests/support.js +181 -0
- package/dist/tests/support.js.map +1 -0
- package/dist/types.d.ts +228 -0
- package/dist/types.js +71 -0
- package/dist/types.js.map +1 -0
- package/dist/validation.d.ts +16 -0
- package/dist/validation.js +59 -0
- package/dist/validation.js.map +1 -0
- package/docs/1.introduction.md +98 -0
- package/docs/2.subscriptions.md +147 -0
- package/docs/3.events.md +79 -0
- package/docs/4.implementing.md +158 -0
- package/docs/5.provider-capability-matrix.md +61 -0
- package/docs/tsconfig.json +14 -0
- package/package.json +80 -4
- package/skills/managing-subscriptions/SKILL.md +154 -0
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { RegisteringProxy } from "@antelopejs/interface-core";
|
|
2
|
+
import type { Subscription, SubscriptionEventHandler, SubscriptionRequest } from "./types";
|
|
3
|
+
/**
|
|
4
|
+
* Starts billing a saved payment method every period.
|
|
5
|
+
*
|
|
6
|
+
* Without a trial or a future billing anchor, the first period is charged
|
|
7
|
+
* before this resolves. A declined first charge creates nothing and rejects
|
|
8
|
+
* with {@link SubscriptionPaymentError}. A first charge that is still settling
|
|
9
|
+
* — a bank debit, say — creates the subscription as `active`; if it then
|
|
10
|
+
* fails, the subscription goes `past_due` like any other failed period.
|
|
11
|
+
*
|
|
12
|
+
* Every charge is an off-session payment-interface payment against
|
|
13
|
+
* `paymentMethod`, carrying {@link SUBSCRIPTION_METADATA_KEY} in its metadata.
|
|
14
|
+
*
|
|
15
|
+
* Implementations must document their replay window and resolve retained
|
|
16
|
+
* replays before checking whether the original trial or billing date has passed.
|
|
17
|
+
*
|
|
18
|
+
* @param request - Who, what, how often, and when to start
|
|
19
|
+
* @param idempotencyKey - Caller-generated key; within the provider-defined
|
|
20
|
+
* replay window, returns the saved outcome (including rejections where
|
|
21
|
+
* retained). After expiration, applications must reconcile uncertain payments
|
|
22
|
+
* and subscription creation before retrying; the same key may charge again.
|
|
23
|
+
* @param provider - Optional payment account selector, passed through to the
|
|
24
|
+
* payment interface. Later calls on the subscription must name the same one.
|
|
25
|
+
* @returns The subscription
|
|
26
|
+
* @throws InvalidSubscriptionRequestError if the request is outside the
|
|
27
|
+
* contract
|
|
28
|
+
* @throws SubscriptionPaymentError if the first period was charged and declined
|
|
29
|
+
*/
|
|
30
|
+
export declare const CreateSubscription: (request: SubscriptionRequest, idempotencyKey: string, provider?: string | undefined) => Promise<Subscription>;
|
|
31
|
+
/**
|
|
32
|
+
* Reads a subscription's current state.
|
|
33
|
+
*
|
|
34
|
+
* @param id - Opaque subscription identifier
|
|
35
|
+
* @param provider - Optional payment account selector
|
|
36
|
+
* @returns The subscription
|
|
37
|
+
* @throws SubscriptionNotFoundError if no subscription has that identifier
|
|
38
|
+
*/
|
|
39
|
+
export declare const GetSubscription: (id: string, provider?: string | undefined) => Promise<Subscription>;
|
|
40
|
+
/**
|
|
41
|
+
* Lists a customer's subscriptions, ended ones included.
|
|
42
|
+
*
|
|
43
|
+
* @param customer - Opaque payment-interface customer identifier
|
|
44
|
+
* @param provider - Optional payment account selector
|
|
45
|
+
* @returns The subscriptions, newest first. Empty when there are none.
|
|
46
|
+
*/
|
|
47
|
+
export declare const ListSubscriptions: (customer: string, provider?: string | undefined) => Promise<Subscription[]>;
|
|
48
|
+
/**
|
|
49
|
+
* Cancels a subscription, now or when the period paid for runs out.
|
|
50
|
+
*
|
|
51
|
+
* Asking for `period_end` on a subscription already set to end then is not an
|
|
52
|
+
* error. Asking for `now` on one set to end at period end ends it now.
|
|
53
|
+
*
|
|
54
|
+
* @param id - Opaque subscription identifier
|
|
55
|
+
* @param idempotencyKey - Caller-generated key; replaying it within the
|
|
56
|
+
* provider-defined replay window returns the saved subscription
|
|
57
|
+
* @param timing - `now`, or `period_end`; see {@link CancellationTiming}
|
|
58
|
+
* @param provider - Optional payment account selector
|
|
59
|
+
* @returns The subscription after the change
|
|
60
|
+
* @throws SubscriptionNotFoundError if no subscription has that identifier
|
|
61
|
+
* @throws InvalidSubscriptionStateError if the subscription has already ended
|
|
62
|
+
*/
|
|
63
|
+
export declare const CancelSubscription: (id: string, idempotencyKey: string, timing: "now" | "period_end", provider?: string | undefined) => Promise<Subscription>;
|
|
64
|
+
/**
|
|
65
|
+
* Charges a different saved method from the next attempt onwards.
|
|
66
|
+
*
|
|
67
|
+
* This is how a `past_due` subscription recovers when the old method will not:
|
|
68
|
+
* save a new one on-session, then switch to it. Nothing is charged by this
|
|
69
|
+
* call; the next scheduled attempt uses the new method.
|
|
70
|
+
*
|
|
71
|
+
* @param id - Opaque subscription identifier
|
|
72
|
+
* @param paymentMethod - Opaque payment-interface identifier of a method saved
|
|
73
|
+
* against the subscription's customer
|
|
74
|
+
* @param idempotencyKey - Caller-generated key; replay is safe within the
|
|
75
|
+
* provider-defined replay window
|
|
76
|
+
* @param provider - Optional payment account selector
|
|
77
|
+
* @returns The subscription after the change
|
|
78
|
+
* @throws SubscriptionNotFoundError if no subscription has that identifier
|
|
79
|
+
* @throws InvalidSubscriptionStateError if the subscription has ended
|
|
80
|
+
*/
|
|
81
|
+
export declare const UpdateSubscriptionPaymentMethod: (id: string, paymentMethod: string, idempotencyKey: string, provider?: string | undefined) => Promise<Subscription>;
|
|
82
|
+
/**
|
|
83
|
+
* Where subscription events are delivered.
|
|
84
|
+
*
|
|
85
|
+
* Register a handler under an identifier unique within your module:
|
|
86
|
+
*
|
|
87
|
+
* ```ts
|
|
88
|
+
* SubscriptionEvents.register("billing", async (event) => { ... });
|
|
89
|
+
* ```
|
|
90
|
+
*
|
|
91
|
+
* Handlers are unregistered when the registering module unloads. Delivery is at
|
|
92
|
+
* least once: an event is delivered again if a handler throws, and may be
|
|
93
|
+
* delivered again after a crash. De-duplicate on
|
|
94
|
+
* {@link SubscriptionEvent.id}. In a cluster, each event is delivered by one
|
|
95
|
+
* instance, to the handlers registered in that instance.
|
|
96
|
+
*
|
|
97
|
+
* How an implementation learns of an event is its own business: the generic
|
|
98
|
+
* fallback raises them from its own clock, and a native provider from the
|
|
99
|
+
* provider's webhooks.
|
|
100
|
+
*/
|
|
101
|
+
export declare const SubscriptionEvents: RegisteringProxy<(id: string, handler: SubscriptionEventHandler) => void>;
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SubscriptionEvents = exports.UpdateSubscriptionPaymentMethod = exports.CancelSubscription = exports.ListSubscriptions = exports.GetSubscription = exports.CreateSubscription = void 0;
|
|
4
|
+
const interface_core_1 = require("@antelopejs/interface-core");
|
|
5
|
+
/**
|
|
6
|
+
* Starts billing a saved payment method every period.
|
|
7
|
+
*
|
|
8
|
+
* Without a trial or a future billing anchor, the first period is charged
|
|
9
|
+
* before this resolves. A declined first charge creates nothing and rejects
|
|
10
|
+
* with {@link SubscriptionPaymentError}. A first charge that is still settling
|
|
11
|
+
* — a bank debit, say — creates the subscription as `active`; if it then
|
|
12
|
+
* fails, the subscription goes `past_due` like any other failed period.
|
|
13
|
+
*
|
|
14
|
+
* Every charge is an off-session payment-interface payment against
|
|
15
|
+
* `paymentMethod`, carrying {@link SUBSCRIPTION_METADATA_KEY} in its metadata.
|
|
16
|
+
*
|
|
17
|
+
* Implementations must document their replay window and resolve retained
|
|
18
|
+
* replays before checking whether the original trial or billing date has passed.
|
|
19
|
+
*
|
|
20
|
+
* @param request - Who, what, how often, and when to start
|
|
21
|
+
* @param idempotencyKey - Caller-generated key; within the provider-defined
|
|
22
|
+
* replay window, returns the saved outcome (including rejections where
|
|
23
|
+
* retained). After expiration, applications must reconcile uncertain payments
|
|
24
|
+
* and subscription creation before retrying; the same key may charge again.
|
|
25
|
+
* @param provider - Optional payment account selector, passed through to the
|
|
26
|
+
* payment interface. Later calls on the subscription must name the same one.
|
|
27
|
+
* @returns The subscription
|
|
28
|
+
* @throws InvalidSubscriptionRequestError if the request is outside the
|
|
29
|
+
* contract
|
|
30
|
+
* @throws SubscriptionPaymentError if the first period was charged and declined
|
|
31
|
+
*/
|
|
32
|
+
exports.CreateSubscription = (0, interface_core_1.InterfaceFunction)();
|
|
33
|
+
/**
|
|
34
|
+
* Reads a subscription's current state.
|
|
35
|
+
*
|
|
36
|
+
* @param id - Opaque subscription identifier
|
|
37
|
+
* @param provider - Optional payment account selector
|
|
38
|
+
* @returns The subscription
|
|
39
|
+
* @throws SubscriptionNotFoundError if no subscription has that identifier
|
|
40
|
+
*/
|
|
41
|
+
exports.GetSubscription = (0, interface_core_1.InterfaceFunction)();
|
|
42
|
+
/**
|
|
43
|
+
* Lists a customer's subscriptions, ended ones included.
|
|
44
|
+
*
|
|
45
|
+
* @param customer - Opaque payment-interface customer identifier
|
|
46
|
+
* @param provider - Optional payment account selector
|
|
47
|
+
* @returns The subscriptions, newest first. Empty when there are none.
|
|
48
|
+
*/
|
|
49
|
+
exports.ListSubscriptions = (0, interface_core_1.InterfaceFunction)();
|
|
50
|
+
/**
|
|
51
|
+
* Cancels a subscription, now or when the period paid for runs out.
|
|
52
|
+
*
|
|
53
|
+
* Asking for `period_end` on a subscription already set to end then is not an
|
|
54
|
+
* error. Asking for `now` on one set to end at period end ends it now.
|
|
55
|
+
*
|
|
56
|
+
* @param id - Opaque subscription identifier
|
|
57
|
+
* @param idempotencyKey - Caller-generated key; replaying it within the
|
|
58
|
+
* provider-defined replay window returns the saved subscription
|
|
59
|
+
* @param timing - `now`, or `period_end`; see {@link CancellationTiming}
|
|
60
|
+
* @param provider - Optional payment account selector
|
|
61
|
+
* @returns The subscription after the change
|
|
62
|
+
* @throws SubscriptionNotFoundError if no subscription has that identifier
|
|
63
|
+
* @throws InvalidSubscriptionStateError if the subscription has already ended
|
|
64
|
+
*/
|
|
65
|
+
exports.CancelSubscription = (0, interface_core_1.InterfaceFunction)();
|
|
66
|
+
/**
|
|
67
|
+
* Charges a different saved method from the next attempt onwards.
|
|
68
|
+
*
|
|
69
|
+
* This is how a `past_due` subscription recovers when the old method will not:
|
|
70
|
+
* save a new one on-session, then switch to it. Nothing is charged by this
|
|
71
|
+
* call; the next scheduled attempt uses the new method.
|
|
72
|
+
*
|
|
73
|
+
* @param id - Opaque subscription identifier
|
|
74
|
+
* @param paymentMethod - Opaque payment-interface identifier of a method saved
|
|
75
|
+
* against the subscription's customer
|
|
76
|
+
* @param idempotencyKey - Caller-generated key; replay is safe within the
|
|
77
|
+
* provider-defined replay window
|
|
78
|
+
* @param provider - Optional payment account selector
|
|
79
|
+
* @returns The subscription after the change
|
|
80
|
+
* @throws SubscriptionNotFoundError if no subscription has that identifier
|
|
81
|
+
* @throws InvalidSubscriptionStateError if the subscription has ended
|
|
82
|
+
*/
|
|
83
|
+
exports.UpdateSubscriptionPaymentMethod = (0, interface_core_1.InterfaceFunction)();
|
|
84
|
+
/**
|
|
85
|
+
* Where subscription events are delivered.
|
|
86
|
+
*
|
|
87
|
+
* Register a handler under an identifier unique within your module:
|
|
88
|
+
*
|
|
89
|
+
* ```ts
|
|
90
|
+
* SubscriptionEvents.register("billing", async (event) => { ... });
|
|
91
|
+
* ```
|
|
92
|
+
*
|
|
93
|
+
* Handlers are unregistered when the registering module unloads. Delivery is at
|
|
94
|
+
* least once: an event is delivered again if a handler throws, and may be
|
|
95
|
+
* delivered again after a crash. De-duplicate on
|
|
96
|
+
* {@link SubscriptionEvent.id}. In a cluster, each event is delivered by one
|
|
97
|
+
* instance, to the handlers registered in that instance.
|
|
98
|
+
*
|
|
99
|
+
* How an implementation learns of an event is its own business: the generic
|
|
100
|
+
* fallback raises them from its own clock, and a native provider from the
|
|
101
|
+
* provider's webhooks.
|
|
102
|
+
*/
|
|
103
|
+
exports.SubscriptionEvents = new interface_core_1.RegisteringProxy();
|
|
104
|
+
//# sourceMappingURL=functions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"functions.js","sourceRoot":"","sources":["../src/functions.ts"],"names":[],"mappings":";;;AAAA,+DAGoC;AASpC;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACU,QAAA,kBAAkB,GAC7B,IAAA,kCAAiB,GAMd,CAAC;AAEN;;;;;;;GAOG;AACU,QAAA,eAAe,GAC1B,IAAA,kCAAiB,GAA4D,CAAC;AAEhF;;;;;;GAMG;AACU,QAAA,iBAAiB,GAC5B,IAAA,kCAAiB,GAEd,CAAC;AAEN;;;;;;;;;;;;;;GAcG;AACU,QAAA,kBAAkB,GAC7B,IAAA,kCAAiB,GAOd,CAAC;AAEN;;;;;;;;;;;;;;;;GAgBG;AACU,QAAA,+BAA+B,GAC1C,IAAA,kCAAiB,GAOd,CAAC;AAEN;;;;;;;;;;;;;;;;;;GAkBG;AACU,QAAA,kBAAkB,GAAG,IAAI,iCAAgB,EAEnD,CAAC"}
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./errors"), exports);
|
|
18
|
+
__exportStar(require("./functions"), exports);
|
|
19
|
+
__exportStar(require("./types"), exports);
|
|
20
|
+
__exportStar(require("./validation"), exports);
|
|
21
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,2CAAyB;AACzB,8CAA4B;AAC5B,0CAAwB;AACxB,+CAA6B"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const strict_1 = __importDefault(require("node:assert/strict"));
|
|
7
|
+
const promises_1 = require("node:timers/promises");
|
|
8
|
+
const support_1 = require("./support");
|
|
9
|
+
const index_1 = require("../index");
|
|
10
|
+
const EVENT_TIMEOUT = 20_000;
|
|
11
|
+
const ABSENT_CUSTOMER = `cus_antelope_absent_${"0".repeat(16)}`;
|
|
12
|
+
const ABSENT_METHOD = `pm_antelope_absent_${"0".repeat(16)}`;
|
|
13
|
+
const EVENT_POLL = 100;
|
|
14
|
+
describe(`cancelling (${support_1.CHARGEABLE})`, function () {
|
|
15
|
+
this.timeout(support_1.SUITE_TIMEOUT);
|
|
16
|
+
before(function () {
|
|
17
|
+
if (!support_1.TEST_METHOD)
|
|
18
|
+
this.skip();
|
|
19
|
+
});
|
|
20
|
+
it("ends a subscription now", async () => {
|
|
21
|
+
const subscription = await (0, support_1.subscribe)(await (0, support_1.newPayer)());
|
|
22
|
+
const canceled = await (0, index_1.CancelSubscription)(subscription.id, (0, support_1.key)(), "now");
|
|
23
|
+
strict_1.default.equal(canceled.status, "canceled");
|
|
24
|
+
strict_1.default.equal(canceled.endReason, "requested");
|
|
25
|
+
strict_1.default.ok(canceled.endedAt >= subscription.createdAt);
|
|
26
|
+
strict_1.default.equal((await (0, index_1.GetSubscription)(subscription.id)).status, "canceled");
|
|
27
|
+
});
|
|
28
|
+
it("lets a period run out instead of renewing it", async () => {
|
|
29
|
+
const subscription = await (0, support_1.subscribe)(await (0, support_1.newPayer)());
|
|
30
|
+
const scheduled = await (0, index_1.CancelSubscription)(subscription.id, (0, support_1.key)(), "period_end");
|
|
31
|
+
strict_1.default.equal(scheduled.status, "active", "the paid period keeps running");
|
|
32
|
+
strict_1.default.equal(scheduled.cancelAtPeriodEnd, true);
|
|
33
|
+
strict_1.default.equal(scheduled.endedAt, undefined);
|
|
34
|
+
strict_1.default.equal(scheduled.currentPeriodEnd, subscription.currentPeriodEnd);
|
|
35
|
+
strict_1.default.equal((await (0, index_1.GetSubscription)(subscription.id)).cancelAtPeriodEnd, true);
|
|
36
|
+
});
|
|
37
|
+
it("accepts a second request to end at period end", async () => {
|
|
38
|
+
const subscription = await (0, support_1.subscribe)(await (0, support_1.newPayer)());
|
|
39
|
+
await (0, index_1.CancelSubscription)(subscription.id, (0, support_1.key)(), "period_end");
|
|
40
|
+
const again = await (0, index_1.CancelSubscription)(subscription.id, (0, support_1.key)(), "period_end");
|
|
41
|
+
strict_1.default.equal(again.cancelAtPeriodEnd, true);
|
|
42
|
+
strict_1.default.equal(again.status, "active");
|
|
43
|
+
});
|
|
44
|
+
it("ends now a subscription already set to end at period end", async () => {
|
|
45
|
+
const subscription = await (0, support_1.subscribe)(await (0, support_1.newPayer)());
|
|
46
|
+
await (0, index_1.CancelSubscription)(subscription.id, (0, support_1.key)(), "period_end");
|
|
47
|
+
const canceled = await (0, index_1.CancelSubscription)(subscription.id, (0, support_1.key)(), "now");
|
|
48
|
+
strict_1.default.equal(canceled.status, "canceled");
|
|
49
|
+
});
|
|
50
|
+
it("ends a trial now without charging", async () => {
|
|
51
|
+
const subscription = await (0, support_1.subscribe)(await (0, support_1.newPayer)(), {
|
|
52
|
+
trialEnd: (0, support_1.inWholeSeconds)(support_1.TRIAL_DAYS * support_1.DAY_MS),
|
|
53
|
+
});
|
|
54
|
+
const canceled = await (0, index_1.CancelSubscription)(subscription.id, (0, support_1.key)(), "now");
|
|
55
|
+
strict_1.default.equal(canceled.status, "canceled");
|
|
56
|
+
strict_1.default.equal(canceled.latestPayment, undefined);
|
|
57
|
+
});
|
|
58
|
+
it("refuses to cancel what has already ended", async () => {
|
|
59
|
+
const subscription = await (0, support_1.subscribe)(await (0, support_1.newPayer)());
|
|
60
|
+
await (0, index_1.CancelSubscription)(subscription.id, (0, support_1.key)(), "now");
|
|
61
|
+
for (const timing of ["now", "period_end"]) {
|
|
62
|
+
const error = await (0, support_1.rejectsWith)((0, index_1.CancelSubscription)(subscription.id, (0, support_1.key)(), timing), index_1.InvalidSubscriptionStateError, `cancel ${timing} after the end`);
|
|
63
|
+
strict_1.default.equal(error.status, "canceled");
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
it("replays a cancellation for a repeated idempotency key", async () => {
|
|
67
|
+
const subscription = await (0, support_1.subscribe)(await (0, support_1.newPayer)());
|
|
68
|
+
const idempotencyKey = (0, support_1.key)();
|
|
69
|
+
const first = await (0, index_1.CancelSubscription)(subscription.id, idempotencyKey, "now");
|
|
70
|
+
const replay = await (0, index_1.CancelSubscription)(subscription.id, idempotencyKey, "now");
|
|
71
|
+
strict_1.default.equal(replay.status, "canceled");
|
|
72
|
+
strict_1.default.equal(replay.endedAt, first.endedAt);
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
describe(`changing the payment method (${support_1.CHARGEABLE})`, function () {
|
|
76
|
+
this.timeout(support_1.SUITE_TIMEOUT);
|
|
77
|
+
before(function () {
|
|
78
|
+
if (!support_1.TEST_METHOD)
|
|
79
|
+
this.skip();
|
|
80
|
+
});
|
|
81
|
+
it("charges a newly saved method from now on", async () => {
|
|
82
|
+
const payer = await (0, support_1.newPayer)();
|
|
83
|
+
const subscription = await (0, support_1.subscribe)(payer);
|
|
84
|
+
const replacement = await (0, support_1.saveMethod)(payer.customer);
|
|
85
|
+
const updated = await (0, index_1.UpdateSubscriptionPaymentMethod)(subscription.id, replacement, (0, support_1.key)());
|
|
86
|
+
strict_1.default.equal(updated.paymentMethod, replacement);
|
|
87
|
+
strict_1.default.equal(updated.latestPayment, subscription.latestPayment, "switching charges nothing");
|
|
88
|
+
strict_1.default.equal((await (0, index_1.GetSubscription)(subscription.id)).paymentMethod, replacement);
|
|
89
|
+
});
|
|
90
|
+
it("refuses to change the method of a subscription that has ended", async () => {
|
|
91
|
+
const payer = await (0, support_1.newPayer)();
|
|
92
|
+
const subscription = await (0, support_1.subscribe)(payer);
|
|
93
|
+
await (0, index_1.CancelSubscription)(subscription.id, (0, support_1.key)(), "now");
|
|
94
|
+
await (0, support_1.rejectsWith)((0, index_1.UpdateSubscriptionPaymentMethod)(subscription.id, payer.paymentMethod, (0, support_1.key)()), index_1.InvalidSubscriptionStateError, "update after the end");
|
|
95
|
+
});
|
|
96
|
+
});
|
|
97
|
+
describe("unknown subscriptions", function () {
|
|
98
|
+
this.timeout(support_1.SUITE_TIMEOUT);
|
|
99
|
+
it("raises SubscriptionNotFoundError for every call on an unknown id", async () => {
|
|
100
|
+
await (0, support_1.rejectsWith)((0, index_1.GetSubscription)(support_1.ABSENT_ID), index_1.SubscriptionNotFoundError, "get");
|
|
101
|
+
await (0, support_1.rejectsWith)((0, index_1.CancelSubscription)(support_1.ABSENT_ID, (0, support_1.key)(), "now"), index_1.SubscriptionNotFoundError, "cancel");
|
|
102
|
+
await (0, support_1.rejectsWith)((0, index_1.UpdateSubscriptionPaymentMethod)(support_1.ABSENT_ID, "pm_absent", (0, support_1.key)()), index_1.SubscriptionNotFoundError, "update");
|
|
103
|
+
});
|
|
104
|
+
});
|
|
105
|
+
describe(`requests outside the contract (${support_1.CHARGEABLE})`, function () {
|
|
106
|
+
this.timeout(support_1.SUITE_TIMEOUT);
|
|
107
|
+
before(function () {
|
|
108
|
+
if (!support_1.TEST_METHOD)
|
|
109
|
+
this.skip();
|
|
110
|
+
});
|
|
111
|
+
const INVALID = {
|
|
112
|
+
"a zero amount": () => ({ amount: (0, support_1.amount)(0) }),
|
|
113
|
+
"a fractional amount": () => ({ amount: (0, support_1.amount)(10.5) }),
|
|
114
|
+
"an interval count of zero": () => ({ intervalCount: 0 }),
|
|
115
|
+
"a fractional interval count": () => ({ intervalCount: 1.5 }),
|
|
116
|
+
"an interval outside the vocabulary": () => ({
|
|
117
|
+
interval: "fortnight",
|
|
118
|
+
}),
|
|
119
|
+
"a trial that has already ended": (now) => ({ trialEnd: now - support_1.DAY_MS }),
|
|
120
|
+
"a billing anchor in the past": (now) => ({
|
|
121
|
+
billingAnchor: now - support_1.DAY_MS,
|
|
122
|
+
}),
|
|
123
|
+
"both a trial and a billing anchor": (now) => ({
|
|
124
|
+
trialEnd: now + support_1.DAY_MS,
|
|
125
|
+
billingAnchor: now + 2 * support_1.DAY_MS,
|
|
126
|
+
}),
|
|
127
|
+
};
|
|
128
|
+
for (const [label, overrides] of Object.entries(INVALID)) {
|
|
129
|
+
it(`refuses ${label}`, async () => {
|
|
130
|
+
const payer = await (0, support_1.newPayer)();
|
|
131
|
+
await (0, support_1.rejectsWith)((0, index_1.CreateSubscription)((0, support_1.monthly)(payer, overrides(Date.now())), (0, support_1.key)()), index_1.InvalidSubscriptionRequestError, label);
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
it("refuses a customer that does not exist, as a request, not a missing subscription", async () => {
|
|
135
|
+
const payer = await (0, support_1.newPayer)();
|
|
136
|
+
await (0, support_1.rejectsWith)((0, index_1.CreateSubscription)((0, support_1.monthly)(payer, { customer: ABSENT_CUSTOMER }), (0, support_1.key)()), index_1.InvalidSubscriptionRequestError, "unknown customer");
|
|
137
|
+
});
|
|
138
|
+
it("refuses a payment method that does not exist", async () => {
|
|
139
|
+
const payer = await (0, support_1.newPayer)();
|
|
140
|
+
await (0, support_1.rejectsWith)((0, index_1.CreateSubscription)((0, support_1.monthly)(payer, { paymentMethod: ABSENT_METHOD }), (0, support_1.key)()), index_1.InvalidSubscriptionRequestError, "unknown method");
|
|
141
|
+
});
|
|
142
|
+
it("refuses to switch to a method that does not exist, and the subscription stays", async () => {
|
|
143
|
+
const subscription = await (0, support_1.subscribe)(await (0, support_1.newPayer)());
|
|
144
|
+
await (0, support_1.rejectsWith)((0, index_1.UpdateSubscriptionPaymentMethod)(subscription.id, ABSENT_METHOD, (0, support_1.key)()), index_1.InvalidSubscriptionRequestError, "switch to an unknown method");
|
|
145
|
+
strict_1.default.equal((await (0, index_1.GetSubscription)(subscription.id)).paymentMethod, subscription.paymentMethod);
|
|
146
|
+
});
|
|
147
|
+
});
|
|
148
|
+
describe(`events (${support_1.OBSERVABLE})`, function () {
|
|
149
|
+
this.timeout(support_1.SUITE_TIMEOUT);
|
|
150
|
+
const received = [];
|
|
151
|
+
const HANDLER_ID = "conformance-suite";
|
|
152
|
+
before(function () {
|
|
153
|
+
if (!support_1.EVENTS_OBSERVABLE || !support_1.TEST_METHOD)
|
|
154
|
+
this.skip();
|
|
155
|
+
index_1.SubscriptionEvents.register(HANDLER_ID, (event) => {
|
|
156
|
+
received.push(event);
|
|
157
|
+
});
|
|
158
|
+
});
|
|
159
|
+
after(() => {
|
|
160
|
+
if (support_1.EVENTS_OBSERVABLE)
|
|
161
|
+
index_1.SubscriptionEvents.unregister(HANDLER_ID);
|
|
162
|
+
});
|
|
163
|
+
async function eventsFor(id, count) {
|
|
164
|
+
const deadline = Date.now() + EVENT_TIMEOUT;
|
|
165
|
+
const matching = () => received.filter((event) => event.subscription.id === id);
|
|
166
|
+
while (matching().length < count && Date.now() < deadline) {
|
|
167
|
+
await (0, promises_1.setTimeout)(EVENT_POLL);
|
|
168
|
+
}
|
|
169
|
+
return matching();
|
|
170
|
+
}
|
|
171
|
+
it("reports a new paid subscription as created, then paid", async () => {
|
|
172
|
+
const subscription = await (0, support_1.subscribe)(await (0, support_1.newPayer)());
|
|
173
|
+
const events = await eventsFor(subscription.id, 2);
|
|
174
|
+
strict_1.default.deepEqual(events.map((event) => event.type).sort(), [
|
|
175
|
+
"subscription.created",
|
|
176
|
+
"subscription.payment_succeeded",
|
|
177
|
+
]);
|
|
178
|
+
const paid = events.find((event) => event.type === "subscription.payment_succeeded");
|
|
179
|
+
strict_1.default.equal(paid?.payment, subscription.latestPayment);
|
|
180
|
+
strict_1.default.equal(new Set(events.map((event) => event.id)).size, events.length);
|
|
181
|
+
});
|
|
182
|
+
it("reports a cancellation", async () => {
|
|
183
|
+
const subscription = await (0, support_1.subscribe)(await (0, support_1.newPayer)(), {
|
|
184
|
+
trialEnd: (0, support_1.inWholeSeconds)(support_1.TRIAL_DAYS * support_1.DAY_MS),
|
|
185
|
+
});
|
|
186
|
+
await (0, index_1.CancelSubscription)(subscription.id, (0, support_1.key)(), "now");
|
|
187
|
+
const events = await eventsFor(subscription.id, 2);
|
|
188
|
+
const canceled = events.find((event) => event.type === "subscription.canceled");
|
|
189
|
+
strict_1.default.ok(canceled, "a cancellation must be reported");
|
|
190
|
+
strict_1.default.equal(canceled.subscription.status, "canceled");
|
|
191
|
+
strict_1.default.equal(canceled.payment, undefined);
|
|
192
|
+
});
|
|
193
|
+
});
|
|
194
|
+
//# sourceMappingURL=subscriptions-changes.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"subscriptions-changes.test.js","sourceRoot":"","sources":["../../src/tests/subscriptions-changes.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const strict_1 = __importDefault(require("node:assert/strict"));
|
|
7
|
+
const promises_1 = require("node:timers/promises");
|
|
8
|
+
const interface_payment_1 = require("@antelopejs/interface-payment");
|
|
9
|
+
const index_1 = require("../index");
|
|
10
|
+
const support_1 = require("./support");
|
|
11
|
+
describe(`creating a subscription (${support_1.CHARGEABLE})`, function () {
|
|
12
|
+
this.timeout(support_1.SUITE_TIMEOUT);
|
|
13
|
+
before(function () {
|
|
14
|
+
if (!support_1.TEST_METHOD)
|
|
15
|
+
this.skip();
|
|
16
|
+
});
|
|
17
|
+
it("charges the first period at once and reports it active", async () => {
|
|
18
|
+
const payer = await (0, support_1.newPayer)();
|
|
19
|
+
const before = Date.now();
|
|
20
|
+
const subscription = await (0, support_1.subscribe)(payer);
|
|
21
|
+
strict_1.default.equal(subscription.status, "active");
|
|
22
|
+
strict_1.default.equal(subscription.cancelAtPeriodEnd, false);
|
|
23
|
+
strict_1.default.equal(subscription.paymentMethod, payer.paymentMethod);
|
|
24
|
+
strict_1.default.ok(subscription.latestPayment, "the first charge must be recorded");
|
|
25
|
+
strict_1.default.ok(subscription.currentPeriodStart <= Date.now());
|
|
26
|
+
strict_1.default.ok(subscription.currentPeriodStart >= before - support_1.DAY_MS);
|
|
27
|
+
const length = subscription.currentPeriodEnd - subscription.currentPeriodStart;
|
|
28
|
+
strict_1.default.ok(length >= support_1.SHORTEST_MONTH_MS && length <= support_1.LONGEST_MONTH_MS, `a one-month period lasted ${length / support_1.DAY_MS} days`);
|
|
29
|
+
});
|
|
30
|
+
it("charges the first period as an off-session payment it can be told by", async () => {
|
|
31
|
+
const payer = await (0, support_1.newPayer)();
|
|
32
|
+
const subscription = await (0, support_1.subscribe)(payer);
|
|
33
|
+
const payment = await (0, support_1.settledPayment)(subscription.latestPayment);
|
|
34
|
+
strict_1.default.equal(payment.status, "succeeded");
|
|
35
|
+
strict_1.default.deepEqual(payment.amount, subscription.amount);
|
|
36
|
+
strict_1.default.equal(payment.customer, payer.customer);
|
|
37
|
+
strict_1.default.equal(payment.metadata?.[index_1.SUBSCRIPTION_METADATA_KEY], subscription.id, "a subscription charge must be distinguishable from a one-off payment");
|
|
38
|
+
});
|
|
39
|
+
it("starts a trial without charging anything", async () => {
|
|
40
|
+
const payer = await (0, support_1.newPayer)();
|
|
41
|
+
const trialEnd = (0, support_1.inWholeSeconds)(support_1.TRIAL_DAYS * support_1.DAY_MS);
|
|
42
|
+
const subscription = await (0, support_1.subscribe)(payer, { trialEnd });
|
|
43
|
+
strict_1.default.equal(subscription.status, "trialing");
|
|
44
|
+
strict_1.default.equal(subscription.trialEnd, trialEnd);
|
|
45
|
+
strict_1.default.equal(subscription.currentPeriodEnd, trialEnd);
|
|
46
|
+
strict_1.default.equal(subscription.latestPayment, undefined, "a trial charges nothing");
|
|
47
|
+
});
|
|
48
|
+
it("defers the first charge to a billing anchor", async () => {
|
|
49
|
+
const payer = await (0, support_1.newPayer)();
|
|
50
|
+
const billingAnchor = (0, support_1.inWholeSeconds)(3 * support_1.DAY_MS);
|
|
51
|
+
const subscription = await (0, support_1.subscribe)(payer, { billingAnchor });
|
|
52
|
+
strict_1.default.equal(subscription.status, "active");
|
|
53
|
+
strict_1.default.equal(subscription.trialEnd, undefined);
|
|
54
|
+
strict_1.default.equal(subscription.currentPeriodEnd, billingAnchor);
|
|
55
|
+
strict_1.default.equal(subscription.latestPayment, undefined);
|
|
56
|
+
});
|
|
57
|
+
it("keeps the request's interval count and metadata", async () => {
|
|
58
|
+
const payer = await (0, support_1.newPayer)();
|
|
59
|
+
const metadata = { plan: "conformance-quarterly" };
|
|
60
|
+
const subscription = await (0, support_1.subscribe)(payer, { intervalCount: 3, metadata });
|
|
61
|
+
strict_1.default.equal(subscription.intervalCount, 3);
|
|
62
|
+
strict_1.default.deepEqual(subscription.metadata, metadata);
|
|
63
|
+
const length = subscription.currentPeriodEnd - subscription.currentPeriodStart;
|
|
64
|
+
strict_1.default.ok(length >= 3 * support_1.SHORTEST_MONTH_MS && length <= 3 * support_1.LONGEST_MONTH_MS, `a three-month period lasted ${length / support_1.DAY_MS} days`);
|
|
65
|
+
});
|
|
66
|
+
it("replays the original subscription for a repeated idempotency key", async () => {
|
|
67
|
+
const payer = await (0, support_1.newPayer)();
|
|
68
|
+
const request = (0, support_1.monthly)(payer);
|
|
69
|
+
const idempotencyKey = (0, support_1.key)();
|
|
70
|
+
const first = await (0, index_1.CreateSubscription)(request, idempotencyKey);
|
|
71
|
+
const replay = await (0, index_1.CreateSubscription)(request, idempotencyKey);
|
|
72
|
+
strict_1.default.equal(replay.id, first.id);
|
|
73
|
+
strict_1.default.equal(replay.latestPayment, first.latestPayment, "replaying must not charge again");
|
|
74
|
+
strict_1.default.equal((await (0, index_1.ListSubscriptions)(payer.customer)).length, 1);
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
describe(`reading subscriptions back (${support_1.CHARGEABLE})`, function () {
|
|
78
|
+
this.timeout(support_1.SUITE_TIMEOUT);
|
|
79
|
+
before(function () {
|
|
80
|
+
if (!support_1.TEST_METHOD)
|
|
81
|
+
this.skip();
|
|
82
|
+
});
|
|
83
|
+
it("reads a subscription back by its identifier", async () => {
|
|
84
|
+
const payer = await (0, support_1.newPayer)();
|
|
85
|
+
const created = await (0, support_1.subscribe)(payer);
|
|
86
|
+
const read = await (0, index_1.GetSubscription)(created.id);
|
|
87
|
+
strict_1.default.equal(read.id, created.id);
|
|
88
|
+
strict_1.default.equal(read.status, created.status);
|
|
89
|
+
strict_1.default.equal(read.currentPeriodStart, created.currentPeriodStart);
|
|
90
|
+
strict_1.default.equal(read.currentPeriodEnd, created.currentPeriodEnd);
|
|
91
|
+
strict_1.default.equal(read.latestPayment, created.latestPayment);
|
|
92
|
+
(0, support_1.assertSubscriptionShape)(read, (0, support_1.monthly)(payer, { reference: created.reference }));
|
|
93
|
+
});
|
|
94
|
+
it("lists a customer's subscriptions newest first, and no one else's", async () => {
|
|
95
|
+
const payer = await (0, support_1.newPayer)();
|
|
96
|
+
const stranger = await (0, support_1.newPayer)();
|
|
97
|
+
const older = await (0, support_1.subscribe)(payer);
|
|
98
|
+
await (0, promises_1.setTimeout)(support_1.PROVIDER_CLOCK_RESOLUTION_MS);
|
|
99
|
+
const newer = await (0, support_1.subscribe)(payer, {
|
|
100
|
+
trialEnd: (0, support_1.inWholeSeconds)(support_1.TRIAL_DAYS * support_1.DAY_MS),
|
|
101
|
+
});
|
|
102
|
+
await (0, support_1.subscribe)(stranger);
|
|
103
|
+
const listed = await (0, index_1.ListSubscriptions)(payer.customer);
|
|
104
|
+
strict_1.default.deepEqual(listed.map((subscription) => subscription.id), [newer.id, older.id]);
|
|
105
|
+
});
|
|
106
|
+
it("lists nothing for a customer without subscriptions", async () => {
|
|
107
|
+
const payer = await (0, support_1.newPayer)();
|
|
108
|
+
strict_1.default.deepEqual(await (0, index_1.ListSubscriptions)(payer.customer), []);
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
const REFUSED_PAYMENT_STATUSES = ["failed", "canceled"];
|
|
112
|
+
describe(`a declined first period (${support_1.DECLINES_LATER})`, function () {
|
|
113
|
+
this.timeout(support_1.SUITE_TIMEOUT);
|
|
114
|
+
before(function () {
|
|
115
|
+
if (!support_1.TEST_METHOD_DECLINES_LATER)
|
|
116
|
+
this.skip();
|
|
117
|
+
});
|
|
118
|
+
it("refuses the subscription and says which payment was declined", async () => {
|
|
119
|
+
const payer = await (0, support_1.newPayer)(support_1.TEST_METHOD_DECLINES_LATER);
|
|
120
|
+
const error = await (0, support_1.rejectsWith)((0, index_1.CreateSubscription)((0, support_1.monthly)(payer), (0, support_1.key)()), index_1.SubscriptionPaymentError, "a declined first period");
|
|
121
|
+
const payment = error.payment;
|
|
122
|
+
strict_1.default.ok(REFUSED_PAYMENT_STATUSES.includes(payment.status), `a declined first charge cannot be ${payment.status}`);
|
|
123
|
+
strict_1.default.equal(payment.failure !== undefined, payment.status === "failed", "a failed payment says why");
|
|
124
|
+
strict_1.default.equal((await (0, interface_payment_1.GetPayment)(payment.id)).status, payment.status);
|
|
125
|
+
strict_1.default.deepEqual(await (0, index_1.ListSubscriptions)(payer.customer), [], "a refused subscription must not exist");
|
|
126
|
+
});
|
|
127
|
+
it("refuses the same way when the idempotency key is replayed", async () => {
|
|
128
|
+
const payer = await (0, support_1.newPayer)(support_1.TEST_METHOD_DECLINES_LATER);
|
|
129
|
+
const request = (0, support_1.monthly)(payer);
|
|
130
|
+
const idempotencyKey = (0, support_1.key)();
|
|
131
|
+
const first = await (0, support_1.rejectsWith)((0, index_1.CreateSubscription)(request, idempotencyKey), index_1.SubscriptionPaymentError, "first attempt");
|
|
132
|
+
const replay = await (0, support_1.rejectsWith)((0, index_1.CreateSubscription)(request, idempotencyKey), index_1.SubscriptionPaymentError, "replay");
|
|
133
|
+
strict_1.default.equal(replay.payment.id, first.payment.id, "a replay must not charge again");
|
|
134
|
+
});
|
|
135
|
+
it("still starts a trial, since a trial charges nothing", async () => {
|
|
136
|
+
const payer = await (0, support_1.newPayer)(support_1.TEST_METHOD_DECLINES_LATER);
|
|
137
|
+
const subscription = await (0, support_1.subscribe)(payer, {
|
|
138
|
+
trialEnd: (0, support_1.inWholeSeconds)(support_1.TRIAL_DAYS * support_1.DAY_MS),
|
|
139
|
+
});
|
|
140
|
+
strict_1.default.equal(subscription.status, "trialing");
|
|
141
|
+
strict_1.default.equal(subscription.amount.value, support_1.PERIOD_VALUE);
|
|
142
|
+
});
|
|
143
|
+
});
|
|
144
|
+
//# sourceMappingURL=subscriptions.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"subscriptions.test.js","sourceRoot":"","sources":["../../src/tests/subscriptions.test.ts"],"names":[],"mappings":"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"}
|