@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.
- package/CHANGELOG.md +601 -0
- package/LICENSE +661 -0
- package/NOTICE +45 -0
- package/README.md +79 -0
- package/dist/deposits.d.ts +52 -0
- package/dist/deposits.d.ts.map +1 -0
- package/dist/deposits.js +71 -0
- package/dist/deposits.js.map +1 -0
- package/dist/index.d.ts +23 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +30 -0
- package/dist/index.js.map +1 -0
- package/dist/migrations/1789800000000-CreateCommerce.d.ts +38 -0
- package/dist/migrations/1789800000000-CreateCommerce.d.ts.map +1 -0
- package/dist/migrations/1789800000000-CreateCommerce.js +152 -0
- package/dist/migrations/1789800000000-CreateCommerce.js.map +1 -0
- package/dist/nestjs/commerce.service.d.ts +122 -0
- package/dist/nestjs/commerce.service.d.ts.map +1 -0
- package/dist/nestjs/commerce.service.js +337 -0
- package/dist/nestjs/commerce.service.js.map +1 -0
- package/dist/nestjs/index.d.ts +18 -0
- package/dist/nestjs/index.d.ts.map +1 -0
- package/dist/nestjs/index.js +27 -0
- package/dist/nestjs/index.js.map +1 -0
- package/dist/nestjs/payment.entity.d.ts +94 -0
- package/dist/nestjs/payment.entity.d.ts.map +1 -0
- package/dist/nestjs/payment.entity.js +181 -0
- package/dist/nestjs/payment.entity.js.map +1 -0
- package/dist/nestjs/payout-account.entity.d.ts +41 -0
- package/dist/nestjs/payout-account.entity.d.ts.map +1 -0
- package/dist/nestjs/payout-account.entity.js +83 -0
- package/dist/nestjs/payout-account.entity.js.map +1 -0
- package/dist/providers/port.d.ts +97 -0
- package/dist/providers/port.d.ts.map +1 -0
- package/dist/providers/port.js +16 -0
- package/dist/providers/port.js.map +1 -0
- package/dist/providers/stripe.d.ts +39 -0
- package/dist/providers/stripe.d.ts.map +1 -0
- package/dist/providers/stripe.js +221 -0
- package/dist/providers/stripe.js.map +1 -0
- package/nestjs/package.json +5 -0
- package/package.json +49 -0
- package/src/deposits.ts +96 -0
- package/src/index.ts +40 -0
- package/src/migrations/1789800000000-CreateCommerce.ts +156 -0
- package/src/nestjs/commerce.service.ts +476 -0
- package/src/nestjs/index.ts +24 -0
- package/src/nestjs/payment.entity.ts +150 -0
- package/src/nestjs/payout-account.entity.ts +56 -0
- package/src/providers/port.ts +108 -0
- 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"}
|