@birtalanrobert/commerce 1.0.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.
Files changed (51) hide show
  1. package/CHANGELOG.md +601 -0
  2. package/LICENSE +661 -0
  3. package/NOTICE +45 -0
  4. package/README.md +79 -0
  5. package/dist/deposits.d.ts +52 -0
  6. package/dist/deposits.d.ts.map +1 -0
  7. package/dist/deposits.js +71 -0
  8. package/dist/deposits.js.map +1 -0
  9. package/dist/index.d.ts +23 -0
  10. package/dist/index.d.ts.map +1 -0
  11. package/dist/index.js +30 -0
  12. package/dist/index.js.map +1 -0
  13. package/dist/migrations/1789800000000-CreateCommerce.d.ts +38 -0
  14. package/dist/migrations/1789800000000-CreateCommerce.d.ts.map +1 -0
  15. package/dist/migrations/1789800000000-CreateCommerce.js +152 -0
  16. package/dist/migrations/1789800000000-CreateCommerce.js.map +1 -0
  17. package/dist/nestjs/commerce.service.d.ts +122 -0
  18. package/dist/nestjs/commerce.service.d.ts.map +1 -0
  19. package/dist/nestjs/commerce.service.js +337 -0
  20. package/dist/nestjs/commerce.service.js.map +1 -0
  21. package/dist/nestjs/index.d.ts +18 -0
  22. package/dist/nestjs/index.d.ts.map +1 -0
  23. package/dist/nestjs/index.js +27 -0
  24. package/dist/nestjs/index.js.map +1 -0
  25. package/dist/nestjs/payment.entity.d.ts +94 -0
  26. package/dist/nestjs/payment.entity.d.ts.map +1 -0
  27. package/dist/nestjs/payment.entity.js +181 -0
  28. package/dist/nestjs/payment.entity.js.map +1 -0
  29. package/dist/nestjs/payout-account.entity.d.ts +41 -0
  30. package/dist/nestjs/payout-account.entity.d.ts.map +1 -0
  31. package/dist/nestjs/payout-account.entity.js +83 -0
  32. package/dist/nestjs/payout-account.entity.js.map +1 -0
  33. package/dist/providers/port.d.ts +97 -0
  34. package/dist/providers/port.d.ts.map +1 -0
  35. package/dist/providers/port.js +16 -0
  36. package/dist/providers/port.js.map +1 -0
  37. package/dist/providers/stripe.d.ts +39 -0
  38. package/dist/providers/stripe.d.ts.map +1 -0
  39. package/dist/providers/stripe.js +221 -0
  40. package/dist/providers/stripe.js.map +1 -0
  41. package/nestjs/package.json +5 -0
  42. package/package.json +49 -0
  43. package/src/deposits.ts +96 -0
  44. package/src/index.ts +40 -0
  45. package/src/migrations/1789800000000-CreateCommerce.ts +156 -0
  46. package/src/nestjs/commerce.service.ts +476 -0
  47. package/src/nestjs/index.ts +24 -0
  48. package/src/nestjs/payment.entity.ts +150 -0
  49. package/src/nestjs/payout-account.entity.ts +56 -0
  50. package/src/providers/port.ts +108 -0
  51. package/src/providers/stripe.ts +274 -0
@@ -0,0 +1,181 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.PaymentRefund = exports.Payment = void 0;
13
+ const typeorm_1 = require("typeorm");
14
+ const database_1 = require("@birtalanrobert/database");
15
+ /**
16
+ * One movement of money between a customer and a business.
17
+ *
18
+ * **The record outlives the provider.** Amounts, dates, what it was for and who
19
+ * decided are all here in full rather than as identifiers to fetch: a business
20
+ * has to be able to produce its own takings years later, when the provider
21
+ * account may be closed, the API version retired, or the vendor replaced.
22
+ *
23
+ * There is deliberately **no foreign key to the subject**. One product takes a
24
+ * deposit against an appointment, another against a seat, a third against a
25
+ * table's tab — and a key to any one of them is precisely what would stop this
26
+ * table being shared.
27
+ */
28
+ let Payment = class Payment extends database_1.BaseEntity {
29
+ tenantId;
30
+ /** What it was for, as the owning product names it: `booking:<id>`. */
31
+ subject;
32
+ method;
33
+ state;
34
+ /** Minor units, and the currency it was taken in. Never a float. */
35
+ amount;
36
+ currency;
37
+ /**
38
+ * Our cut, taken on top rather than out of the business's money.
39
+ *
40
+ * Recorded even when zero, because "this product charged nothing for that
41
+ * transaction" and "nobody wrote down what it charged" are different facts
42
+ * and only one of them survives an argument.
43
+ */
44
+ applicationFee;
45
+ /** Sum of everything given back. Never more than `amount`. */
46
+ refunded;
47
+ provider;
48
+ /** The provider's identifier, which is what a webhook arrives carrying. */
49
+ externalId;
50
+ /**
51
+ * The last four digits and the brand, for a person to recognise it by.
52
+ *
53
+ * Never the number, never a token that could be charged from a database
54
+ * dump. What a receptionist needs is "Visa ending 4242", and what a customer
55
+ * needs is to know which of their cards was used.
56
+ */
57
+ instrument;
58
+ /** When the money actually moved, which is not when the row was created. */
59
+ takenAt;
60
+ /** Why it failed, or what a person recorded about a cash payment. */
61
+ detail;
62
+ /** Who recorded it, for the payments a person entered by hand. */
63
+ recordedBy;
64
+ };
65
+ exports.Payment = Payment;
66
+ __decorate([
67
+ (0, typeorm_1.Column)('uuid'),
68
+ __metadata("design:type", String)
69
+ ], Payment.prototype, "tenantId", void 0);
70
+ __decorate([
71
+ (0, typeorm_1.Column)('varchar', { length: 160 }),
72
+ __metadata("design:type", String)
73
+ ], Payment.prototype, "subject", void 0);
74
+ __decorate([
75
+ (0, typeorm_1.Column)('varchar', { length: 16 }),
76
+ __metadata("design:type", String)
77
+ ], Payment.prototype, "method", void 0);
78
+ __decorate([
79
+ (0, typeorm_1.Column)('varchar', { length: 24, default: 'pending' }),
80
+ __metadata("design:type", String)
81
+ ], Payment.prototype, "state", void 0);
82
+ __decorate([
83
+ (0, typeorm_1.Column)(database_1.MONEY_AMOUNT_COLUMN),
84
+ __metadata("design:type", String)
85
+ ], Payment.prototype, "amount", void 0);
86
+ __decorate([
87
+ (0, typeorm_1.Column)('varchar', { length: 3 }),
88
+ __metadata("design:type", String)
89
+ ], Payment.prototype, "currency", void 0);
90
+ __decorate([
91
+ (0, typeorm_1.Column)({ ...database_1.MONEY_AMOUNT_COLUMN, default: '0' }),
92
+ __metadata("design:type", String)
93
+ ], Payment.prototype, "applicationFee", void 0);
94
+ __decorate([
95
+ (0, typeorm_1.Column)({ ...database_1.MONEY_AMOUNT_COLUMN, default: '0' }),
96
+ __metadata("design:type", String)
97
+ ], Payment.prototype, "refunded", void 0);
98
+ __decorate([
99
+ (0, typeorm_1.Column)('varchar', { length: 32, nullable: true }),
100
+ __metadata("design:type", Object)
101
+ ], Payment.prototype, "provider", void 0);
102
+ __decorate([
103
+ (0, typeorm_1.Column)('varchar', { length: 128, nullable: true }),
104
+ __metadata("design:type", Object)
105
+ ], Payment.prototype, "externalId", void 0);
106
+ __decorate([
107
+ (0, typeorm_1.Column)('varchar', { length: 40, nullable: true }),
108
+ __metadata("design:type", Object)
109
+ ], Payment.prototype, "instrument", void 0);
110
+ __decorate([
111
+ (0, typeorm_1.Column)('timestamptz', { nullable: true }),
112
+ __metadata("design:type", Object)
113
+ ], Payment.prototype, "takenAt", void 0);
114
+ __decorate([
115
+ (0, typeorm_1.Column)('varchar', { length: 400, nullable: true }),
116
+ __metadata("design:type", Object)
117
+ ], Payment.prototype, "detail", void 0);
118
+ __decorate([
119
+ (0, typeorm_1.Column)('uuid', { nullable: true }),
120
+ __metadata("design:type", Object)
121
+ ], Payment.prototype, "recordedBy", void 0);
122
+ exports.Payment = Payment = __decorate([
123
+ (0, typeorm_1.Entity)('mortar_payments'),
124
+ (0, typeorm_1.Unique)('uq_payments_tenant_id', ['tenantId', 'id']),
125
+ (0, typeorm_1.Index)('ix_payments_subject', ['tenantId', 'subject']),
126
+ (0, typeorm_1.Index)('ix_payments_taken', ['tenantId', 'takenAt']),
127
+ (0, typeorm_1.Index)('ix_payments_external', ['provider', 'externalId'])
128
+ ], Payment);
129
+ /**
130
+ * Money given back, one row per act of giving it.
131
+ *
132
+ * Several partial refunds against one payment is ordinary — a deposit returned
133
+ * in part, a ticket refunded and its fee kept — and a single `refunded` column
134
+ * cannot say when each happened or why. The column stays as the running total,
135
+ * because that is what every read wants, and these rows are what explain it.
136
+ */
137
+ let PaymentRefund = class PaymentRefund extends database_1.BaseEntity {
138
+ tenantId;
139
+ paymentId;
140
+ amount;
141
+ /**
142
+ * Why, in the words of whoever decided.
143
+ *
144
+ * Required rather than optional: "we refunded her ninety lei in March" is a
145
+ * question somebody asks a year later, and a blank reason is an answer
146
+ * nobody can defend.
147
+ */
148
+ reason;
149
+ externalId;
150
+ refundedBy;
151
+ };
152
+ exports.PaymentRefund = PaymentRefund;
153
+ __decorate([
154
+ (0, typeorm_1.Column)('uuid'),
155
+ __metadata("design:type", String)
156
+ ], PaymentRefund.prototype, "tenantId", void 0);
157
+ __decorate([
158
+ (0, typeorm_1.Column)('uuid'),
159
+ __metadata("design:type", String)
160
+ ], PaymentRefund.prototype, "paymentId", void 0);
161
+ __decorate([
162
+ (0, typeorm_1.Column)(database_1.MONEY_AMOUNT_COLUMN),
163
+ __metadata("design:type", String)
164
+ ], PaymentRefund.prototype, "amount", void 0);
165
+ __decorate([
166
+ (0, typeorm_1.Column)('varchar', { length: 400 }),
167
+ __metadata("design:type", String)
168
+ ], PaymentRefund.prototype, "reason", void 0);
169
+ __decorate([
170
+ (0, typeorm_1.Column)('varchar', { length: 128, nullable: true }),
171
+ __metadata("design:type", Object)
172
+ ], PaymentRefund.prototype, "externalId", void 0);
173
+ __decorate([
174
+ (0, typeorm_1.Column)('uuid', { nullable: true }),
175
+ __metadata("design:type", Object)
176
+ ], PaymentRefund.prototype, "refundedBy", void 0);
177
+ exports.PaymentRefund = PaymentRefund = __decorate([
178
+ (0, typeorm_1.Entity)('mortar_payment_refunds'),
179
+ (0, typeorm_1.Index)('ix_payment_refunds_payment', ['tenantId', 'paymentId'])
180
+ ], PaymentRefund);
181
+ //# sourceMappingURL=payment.entity.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"payment.entity.js","sourceRoot":"","sources":["../../src/nestjs/payment.entity.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,41 @@
1
+ import { BaseEntity } from '@birtalanrobert/database';
2
+ import type { PayoutStatus } from '../deposits';
3
+ /**
4
+ * Where a business's money goes, and whether the provider will send it yet.
5
+ *
6
+ * **We never hold anybody's funds.** A customer pays the business directly and
7
+ * our fee is taken on top as an application fee — which is a hard architectural
8
+ * rule rather than a preference, because holding third-party money turns a
9
+ * software company into a regulated payments business.
10
+ *
11
+ * The consequence is this table. Until the provider has verified who the
12
+ * business is, there is nowhere for a payment to land, so every product that
13
+ * takes money on somebody's behalf has to gate its selling on the same fact.
14
+ */
15
+ export declare class PayoutAccount extends BaseEntity {
16
+ tenantId: string;
17
+ /**
18
+ * Which provider this account is with.
19
+ *
20
+ * A column rather than an assumption, because the markets differ: a local
21
+ * processor with faster onboarding beats a lower fee for a restaurant that
22
+ * wants to be live this afternoon, and one of the seventeen will need one.
23
+ */
24
+ provider: string;
25
+ /** The provider's own identifier for the account. */
26
+ externalId: string;
27
+ status: PayoutStatus;
28
+ /**
29
+ * What the provider still wants, in its own words.
30
+ *
31
+ * Kept verbatim rather than translated into a status of ours. "We need a
32
+ * photograph of the director's identity document" is actionable; "restricted"
33
+ * is a support conversation, and the difference is a business that finishes
34
+ * onboarding on a Sunday evening rather than on Tuesday when somebody
35
+ * telephones them.
36
+ */
37
+ requirements: string[];
38
+ /** Set the first time the provider said it would pay out. */
39
+ readyAt: Date | null;
40
+ }
41
+ //# sourceMappingURL=payout-account.entity.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"payout-account.entity.d.ts","sourceRoot":"","sources":["../../src/nestjs/payout-account.entity.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AACtD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD;;;;;;;;;;;GAWG;AACH,qBAGa,aAAc,SAAQ,UAAU;IAE3C,QAAQ,EAAG,MAAM,CAAC;IAElB;;;;;;OAMG;IAEH,QAAQ,EAAG,MAAM,CAAC;IAElB,qDAAqD;IAErD,UAAU,EAAG,MAAM,CAAC;IAGpB,MAAM,EAAG,YAAY,CAAC;IAEtB;;;;;;;;OAQG;IAEH,YAAY,EAAG,MAAM,EAAE,CAAC;IAExB,6DAA6D;IAE7D,OAAO,EAAG,IAAI,GAAG,IAAI,CAAC;CACvB"}
@@ -0,0 +1,83 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.PayoutAccount = void 0;
13
+ const typeorm_1 = require("typeorm");
14
+ const database_1 = require("@birtalanrobert/database");
15
+ /**
16
+ * Where a business's money goes, and whether the provider will send it yet.
17
+ *
18
+ * **We never hold anybody's funds.** A customer pays the business directly and
19
+ * our fee is taken on top as an application fee — which is a hard architectural
20
+ * rule rather than a preference, because holding third-party money turns a
21
+ * software company into a regulated payments business.
22
+ *
23
+ * The consequence is this table. Until the provider has verified who the
24
+ * business is, there is nowhere for a payment to land, so every product that
25
+ * takes money on somebody's behalf has to gate its selling on the same fact.
26
+ */
27
+ let PayoutAccount = class PayoutAccount extends database_1.BaseEntity {
28
+ tenantId;
29
+ /**
30
+ * Which provider this account is with.
31
+ *
32
+ * A column rather than an assumption, because the markets differ: a local
33
+ * processor with faster onboarding beats a lower fee for a restaurant that
34
+ * wants to be live this afternoon, and one of the seventeen will need one.
35
+ */
36
+ provider;
37
+ /** The provider's own identifier for the account. */
38
+ externalId;
39
+ status;
40
+ /**
41
+ * What the provider still wants, in its own words.
42
+ *
43
+ * Kept verbatim rather than translated into a status of ours. "We need a
44
+ * photograph of the director's identity document" is actionable; "restricted"
45
+ * is a support conversation, and the difference is a business that finishes
46
+ * onboarding on a Sunday evening rather than on Tuesday when somebody
47
+ * telephones them.
48
+ */
49
+ requirements;
50
+ /** Set the first time the provider said it would pay out. */
51
+ readyAt;
52
+ };
53
+ exports.PayoutAccount = PayoutAccount;
54
+ __decorate([
55
+ (0, typeorm_1.Column)('uuid'),
56
+ __metadata("design:type", String)
57
+ ], PayoutAccount.prototype, "tenantId", void 0);
58
+ __decorate([
59
+ (0, typeorm_1.Column)('varchar', { length: 32, default: 'stripe' }),
60
+ __metadata("design:type", String)
61
+ ], PayoutAccount.prototype, "provider", void 0);
62
+ __decorate([
63
+ (0, typeorm_1.Column)('varchar', { length: 128 }),
64
+ __metadata("design:type", String)
65
+ ], PayoutAccount.prototype, "externalId", void 0);
66
+ __decorate([
67
+ (0, typeorm_1.Column)('varchar', { length: 16, default: 'pending' }),
68
+ __metadata("design:type", String)
69
+ ], PayoutAccount.prototype, "status", void 0);
70
+ __decorate([
71
+ (0, typeorm_1.Column)('jsonb', { default: () => `'[]'::jsonb` }),
72
+ __metadata("design:type", Array)
73
+ ], PayoutAccount.prototype, "requirements", void 0);
74
+ __decorate([
75
+ (0, typeorm_1.Column)('timestamptz', { nullable: true }),
76
+ __metadata("design:type", Object)
77
+ ], PayoutAccount.prototype, "readyAt", void 0);
78
+ exports.PayoutAccount = PayoutAccount = __decorate([
79
+ (0, typeorm_1.Entity)('mortar_payout_accounts'),
80
+ (0, typeorm_1.Unique)('uq_payout_accounts_tenant', ['tenantId', 'provider']),
81
+ (0, typeorm_1.Index)('ix_payout_accounts_external', ['provider', 'externalId'])
82
+ ], PayoutAccount);
83
+ //# sourceMappingURL=payout-account.entity.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"payout-account.entity.js","sourceRoot":"","sources":["../../src/nestjs/payout-account.entity.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,qCAAwD;AACxD,uDAAsD;AAGtD;;;;;;;;;;;GAWG;AAII,IAAM,aAAa,GAAnB,MAAM,aAAc,SAAQ,qBAAU;IAE3C,QAAQ,CAAU;IAElB;;;;;;OAMG;IAEH,QAAQ,CAAU;IAElB,qDAAqD;IAErD,UAAU,CAAU;IAGpB,MAAM,CAAgB;IAEtB;;;;;;;;OAQG;IAEH,YAAY,CAAY;IAExB,6DAA6D;IAE7D,OAAO,CAAe;CACvB,CAAA;AApCY,sCAAa;AAExB;IADC,IAAA,gBAAM,EAAC,MAAM,CAAC;;+CACG;AAUlB;IADC,IAAA,gBAAM,EAAC,SAAS,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC;;+CACnC;AAIlB;IADC,IAAA,gBAAM,EAAC,SAAS,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC;;iDACf;AAGpB;IADC,IAAA,gBAAM,EAAC,SAAS,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC;;6CAChC;AAYtB;IADC,IAAA,gBAAM,EAAC,OAAO,EAAE,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC,aAAa,EAAE,CAAC;;mDAC1B;AAIxB;IADC,IAAA,gBAAM,EAAC,aAAa,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;8CACpB;wBAnCX,aAAa;IAHzB,IAAA,gBAAM,EAAC,wBAAwB,CAAC;IAChC,IAAA,gBAAM,EAAC,2BAA2B,EAAE,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC;IAC7D,IAAA,eAAK,EAAC,6BAA6B,EAAE,CAAC,UAAU,EAAE,YAAY,CAAC,CAAC;GACpD,aAAa,CAoCzB"}
@@ -0,0 +1,97 @@
1
+ /**
2
+ * What a payment provider has to be able to do, and nothing more.
3
+ *
4
+ * A port rather than the vendor's client, for the reason `@birtalanrobert/files`
5
+ * has one over S3: the day a market needs a local processor — and one will,
6
+ * because a restaurant that can be onboarded this afternoon beats a lower fee —
7
+ * nothing above this interface moves.
8
+ *
9
+ * Deliberately small. Everything that can be decided without the provider is
10
+ * decided without it: what a deposit comes to, whether a business may sell yet,
11
+ * what a refund leaves. This is only the part that genuinely needs somebody
12
+ * else's money-moving licence.
13
+ */
14
+ /** Where a business's money goes, as the provider knows it. */
15
+ export interface ProviderAccount {
16
+ readonly externalId: string;
17
+ readonly status: 'pending' | 'restricted' | 'ready';
18
+ /** What the provider still wants, in its own words. */
19
+ readonly requirements: readonly string[];
20
+ }
21
+ export interface OnboardingLink {
22
+ readonly url: string;
23
+ readonly expiresAt: Date;
24
+ }
25
+ export interface ChargeRequest {
26
+ /** The business being paid, as the provider knows it. */
27
+ readonly account: string;
28
+ readonly amount: number;
29
+ readonly currency: string;
30
+ /** Our cut, taken on top rather than out of the business's money. */
31
+ readonly applicationFee: number;
32
+ /** What it is for, carried through so a webhook can be matched back. */
33
+ readonly subject: string;
34
+ /**
35
+ * Whether to take the money now or only hold it.
36
+ *
37
+ * Holding is the anti-no-show mechanism: a card is authorised and charged
38
+ * only if a fee is actually applied, and **that decision is a human one**.
39
+ */
40
+ readonly capture: boolean;
41
+ readonly description?: string;
42
+ /** An idempotency key, so a retried request does not charge twice. */
43
+ readonly reference: string;
44
+ }
45
+ export interface ChargeResult {
46
+ readonly externalId: string;
47
+ readonly state: 'pending' | 'authorized' | 'captured' | 'failed';
48
+ /**
49
+ * Where to send the customer to finish, when the provider needs them.
50
+ *
51
+ * 3-D Secure and bank redirects are the ordinary case in both target
52
+ * markets rather than an exception, so a charge that returns a URL is not a
53
+ * failure and must not be handled as one.
54
+ */
55
+ readonly redirectUrl?: string;
56
+ readonly instrument?: string;
57
+ readonly detail?: string;
58
+ }
59
+ export interface RefundRequest {
60
+ readonly externalId: string;
61
+ readonly amount: number;
62
+ readonly reason: string;
63
+ readonly reference: string;
64
+ }
65
+ /** What a provider's webhook turned out to be about. */
66
+ export interface ProviderEvent {
67
+ readonly kind: 'payment' | 'account' | 'other';
68
+ readonly externalId: string;
69
+ readonly state?: 'authorized' | 'captured' | 'failed' | 'refunded';
70
+ readonly accountStatus?: ProviderAccount;
71
+ readonly instrument?: string;
72
+ readonly detail?: string;
73
+ }
74
+ export interface PaymentProvider {
75
+ readonly name: string;
76
+ /** Starts or resumes onboarding, and says where to send the business. */
77
+ onboard(tenantId: string, returnUrl: string, refreshUrl: string): Promise<OnboardingLink>;
78
+ /** Creates the account if there is none, and reports where it stands. */
79
+ account(externalId: string | null, country: string, email?: string): Promise<ProviderAccount>;
80
+ charge(request: ChargeRequest): Promise<ChargeResult>;
81
+ /** Takes money that was only held. The human decision has been made. */
82
+ capture(externalId: string, amount?: number): Promise<ChargeResult>;
83
+ /** Releases a hold without taking anything. */
84
+ release(externalId: string): Promise<void>;
85
+ refund(request: RefundRequest): Promise<{
86
+ externalId: string;
87
+ }>;
88
+ /**
89
+ * Whether a webhook really came from the provider, and what it says.
90
+ *
91
+ * `undefined` rather than a thrown error: the caller's answer to a request
92
+ * that did not come from the provider is a flat acknowledgement, not a
93
+ * message describing what was wrong with the forgery.
94
+ */
95
+ verify(payload: string | Buffer, signature: string | undefined): ProviderEvent | undefined;
96
+ }
97
+ //# sourceMappingURL=port.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"port.d.ts","sourceRoot":"","sources":["../../src/providers/port.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+ /**
3
+ * What a payment provider has to be able to do, and nothing more.
4
+ *
5
+ * A port rather than the vendor's client, for the reason `@birtalanrobert/files`
6
+ * has one over S3: the day a market needs a local processor — and one will,
7
+ * because a restaurant that can be onboarded this afternoon beats a lower fee —
8
+ * nothing above this interface moves.
9
+ *
10
+ * Deliberately small. Everything that can be decided without the provider is
11
+ * decided without it: what a deposit comes to, whether a business may sell yet,
12
+ * what a refund leaves. This is only the part that genuinely needs somebody
13
+ * else's money-moving licence.
14
+ */
15
+ Object.defineProperty(exports, "__esModule", { value: true });
16
+ //# sourceMappingURL=port.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"port.js","sourceRoot":"","sources":["../../src/providers/port.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;GAYG"}
@@ -0,0 +1,39 @@
1
+ import Stripe from 'stripe';
2
+ import type { ChargeRequest, ChargeResult, OnboardingLink, PaymentProvider, ProviderAccount, ProviderEvent, RefundRequest } from './port';
3
+ export interface StripeConnectOptions {
4
+ secretKey: string;
5
+ /** The endpoint's signing secret, as the dashboard gives it: `whsec_…`. */
6
+ webhookSecret?: string;
7
+ /** An already-built client, for tests and for a deployment that shares one. */
8
+ client?: Stripe;
9
+ }
10
+ /**
11
+ * Stripe Connect, as the port describes it.
12
+ *
13
+ * **Destination charges throughout.** The money is created on our platform
14
+ * account and transferred immediately to the business, with our cut taken as an
15
+ * application fee — which is what keeps this a software company rather than a
16
+ * regulated one, and what lets the business see its own payouts in its own
17
+ * Stripe dashboard.
18
+ *
19
+ * Everything the provider does *not* need to decide is decided before this file
20
+ * is reached: what a deposit comes to, whether a business may sell, what a
21
+ * refund leaves. What is here is the part that genuinely needs somebody else's
22
+ * money-moving licence.
23
+ */
24
+ export declare class StripeConnect implements PaymentProvider {
25
+ private readonly options;
26
+ readonly name = "stripe";
27
+ private readonly stripe;
28
+ constructor(options: StripeConnectOptions);
29
+ account(externalId: string | null, country: string, email?: string): Promise<ProviderAccount>;
30
+ onboard(externalId: string, returnUrl: string, refreshUrl: string): Promise<OnboardingLink>;
31
+ charge(request: ChargeRequest): Promise<ChargeResult>;
32
+ capture(externalId: string, amount?: number): Promise<ChargeResult>;
33
+ release(externalId: string): Promise<void>;
34
+ refund(request: RefundRequest): Promise<{
35
+ externalId: string;
36
+ }>;
37
+ verify(payload: string | Buffer, signature: string | undefined): ProviderEvent | undefined;
38
+ }
39
+ //# sourceMappingURL=stripe.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stripe.d.ts","sourceRoot":"","sources":["../../src/providers/stripe.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,QAAQ,CAAC;AAC5B,OAAO,KAAK,EACV,aAAa,EACb,YAAY,EACZ,cAAc,EACd,eAAe,EACf,eAAe,EACf,aAAa,EACb,aAAa,EACd,MAAM,QAAQ,CAAC;AAEhB,MAAM,WAAW,oBAAoB;IACnC,SAAS,EAAE,MAAM,CAAC;IAClB,2EAA2E;IAC3E,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,+EAA+E;IAC/E,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;;;;;;;;GAaG;AACH,qBAAa,aAAc,YAAW,eAAe;IAIvC,OAAO,CAAC,QAAQ,CAAC,OAAO;IAHpC,QAAQ,CAAC,IAAI,YAAY;IACzB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;gBAEH,OAAO,EAAE,oBAAoB;IAIpD,OAAO,CACX,UAAU,EAAE,MAAM,GAAG,IAAI,EACzB,OAAO,EAAE,MAAM,EACf,KAAK,CAAC,EAAE,MAAM,GACb,OAAO,CAAC,eAAe,CAAC;IAarB,OAAO,CACX,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,cAAc,CAAC;IAkBpB,MAAM,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,YAAY,CAAC;IAiCrD,OAAO,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC;IAanE,OAAO,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAI1C,MAAM,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC;QAAE,UAAU,EAAE,MAAM,CAAA;KAAE,CAAC;IAarE,MAAM,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,SAAS,GAAG,aAAa,GAAG,SAAS;CAoB3F"}