@phis/contracts 0.6.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/addon.d.ts +47 -0
- package/dist/capabilities.d.ts +51 -0
- package/dist/core.d.ts +2 -0
- package/dist/core.js +2 -0
- package/dist/payments.d.ts +111 -0
- package/dist/payments.js +4 -0
- package/package.json +1 -1
package/dist/addon.d.ts
CHANGED
|
@@ -199,6 +199,46 @@ export type PhisAddonEventHandlerDescriptor = {
|
|
|
199
199
|
event: PhisAddonEventId;
|
|
200
200
|
handler: string;
|
|
201
201
|
};
|
|
202
|
+
/**
|
|
203
|
+
* One sentence of an occasion, with the parameters it takes in order.
|
|
204
|
+
*
|
|
205
|
+
* `%1`, `%2`, ... in `text` stand for the named parameters, the way Core's own templates are written,
|
|
206
|
+
* so the sentence translates as one unit and the values are put in afterwards. A text parameter is
|
|
207
|
+
* escaped where it lands; a path parameter arrives as a link on the Site's own public address.
|
|
208
|
+
*/
|
|
209
|
+
export type PhisAddonOccasionLine = {
|
|
210
|
+
text: string;
|
|
211
|
+
parameters?: string[];
|
|
212
|
+
};
|
|
213
|
+
/**
|
|
214
|
+
* One kind of message this Add-on may ask Core to write to a person.
|
|
215
|
+
*
|
|
216
|
+
* Declared rather than composed at the call: the words are part of the manifest and therefore of the
|
|
217
|
+
* digest, so what a Site can say in an Add-on's name is readable at install and cannot change under a
|
|
218
|
+
* running Site. Core renders the lines through its own layout, salutation and translation, and adds
|
|
219
|
+
* what the class owes -- which is why an Add-on cannot produce a mail without Core's frame.
|
|
220
|
+
*
|
|
221
|
+
* `class` is `asked-for` and nothing else this release: the person's own action is the reason for the
|
|
222
|
+
* message (an order they placed, a request they made), no consent bit applies and no unsubscribe link
|
|
223
|
+
* is offered. The sent class -- a message somebody decided to send to people who did not ask today --
|
|
224
|
+
* is Core's own bulk case and is not declarable here until it is.
|
|
225
|
+
*/
|
|
226
|
+
export type PhisAddonOccasionDescriptor = {
|
|
227
|
+
/** `^[a-z][a-z0-9-]{0,63}$`; the name a `notify` call and the outbox row carry. */
|
|
228
|
+
name: string;
|
|
229
|
+
class: "asked-for";
|
|
230
|
+
description?: string;
|
|
231
|
+
subject: PhisAddonOccasionLine;
|
|
232
|
+
lines: PhisAddonOccasionLine[];
|
|
233
|
+
/**
|
|
234
|
+
* What a call must bring, by name. `text` is inserted as written; `path` is a Site-relative path
|
|
235
|
+
* (`/shop/order/1234`) that Core turns into an absolute link on the Site's public address.
|
|
236
|
+
*/
|
|
237
|
+
parameters: {
|
|
238
|
+
name: string;
|
|
239
|
+
type: "text" | "path";
|
|
240
|
+
}[];
|
|
241
|
+
};
|
|
202
242
|
export type PhisAddonManifestV1 = {
|
|
203
243
|
manifestVersion: typeof PHIS_ADDON_MANIFEST_VERSION;
|
|
204
244
|
addonId: string;
|
|
@@ -260,6 +300,13 @@ export type PhisAddonManifestV1 = {
|
|
|
260
300
|
* provider, and every write here refuses a group carrying anybody else's.
|
|
261
301
|
*/
|
|
262
302
|
groupWriter?: PhisAddonGroupWriterDescriptor;
|
|
303
|
+
/**
|
|
304
|
+
* The occasions this Add-on may write to a person about. Absent means none, and `notify` then has
|
|
305
|
+
* nothing it could be asked for.
|
|
306
|
+
*
|
|
307
|
+
* Optional for the reason `settings` is: it arrived after the manifest version was in the wild.
|
|
308
|
+
*/
|
|
309
|
+
occasions?: PhisAddonOccasionDescriptor[];
|
|
263
310
|
/**
|
|
264
311
|
* The Modules this package's other half contributes. Absent means it has no Module half.
|
|
265
312
|
*
|
package/dist/capabilities.d.ts
CHANGED
|
@@ -107,6 +107,8 @@ export type PhisAddonCapabilities = {
|
|
|
107
107
|
payments?: PhisPaymentsCapabilityV1;
|
|
108
108
|
/** Supplied only to an original, bound Payment Provider hook request. */
|
|
109
109
|
paymentNotifications?: PhisPaymentNotificationsCapabilityV1;
|
|
110
|
+
/** Writing to a person about an occasion this Add-on declared; Core owns the frame and the sending. */
|
|
111
|
+
notifications?: PhisNotificationsCapabilityV1;
|
|
110
112
|
};
|
|
111
113
|
/**
|
|
112
114
|
* The roles capability: `@phis/server/roles:v1`.
|
|
@@ -224,6 +226,55 @@ export type PhisSecretsCapabilityV1 = {
|
|
|
224
226
|
};
|
|
225
227
|
/** Which secret `PhisSecretsCapabilityV1.read` names: the Site's own, or the one shared by all Sites. */
|
|
226
228
|
export type PhisSecretScope = "site" | "addon";
|
|
229
|
+
/**
|
|
230
|
+
* The notifications capability: `@phis/server/notifications:v1`.
|
|
231
|
+
*
|
|
232
|
+
* `notify` names **who** and **what happened**, and nothing about how. The occasion is one this Add-on
|
|
233
|
+
* declared in its manifest (`occasions`), so its words are sealed by the digest; Core resolves the
|
|
234
|
+
* address, picks the language, writes the salutation, renders the lines through its own layout and
|
|
235
|
+
* translation, chooses the sender, and sends -- through the outbox, with retry and a record. There is no
|
|
236
|
+
* subject, body, `from`, header or attachment on the call, because every one of those is a decision
|
|
237
|
+
* about how a Site speaks, and a Site speaks with one voice.
|
|
238
|
+
*
|
|
239
|
+
* Two recipients. A `user` is a member of this Site, named by id: the address is resolved when the
|
|
240
|
+
* message is sent, so a corrected address is used and a membership that is gone is not written to.
|
|
241
|
+
* A `contact` is a person who has no account here and gave this Add-on their address for exactly this
|
|
242
|
+
* purpose -- a guest who placed an order, somebody who filled in a form. It is the one place an Add-on
|
|
243
|
+
* hands Core an address, and it is admitted on two conditions Core enforces: the occasion is one the
|
|
244
|
+
* person's own action asked for (every declared occasion is, this release), and the locale is stated,
|
|
245
|
+
* because there is no profile to ask.
|
|
246
|
+
*
|
|
247
|
+
* `params` are values about the event, never about the person: a `text` parameter is inserted as
|
|
248
|
+
* written, a `path` parameter is a Site-relative path Core turns into an absolute link on the Site's own
|
|
249
|
+
* public address. An occasion cannot put an arbitrary URL into a Site's outgoing mail.
|
|
250
|
+
*
|
|
251
|
+
* Answers the outbox row's id, or `null` when `dedupeKey` named a message already written -- which is
|
|
252
|
+
* what makes a repeated run safe. Refused outright: an undeclared occasion, a parameter the occasion
|
|
253
|
+
* did not declare or lacks, a `user` who is not a member of this Site, and any call from an
|
|
254
|
+
* unverified hook, which may change nothing.
|
|
255
|
+
*/
|
|
256
|
+
export type PhisNotificationsCapabilityV1 = {
|
|
257
|
+
notify(input: PhisNotificationInput): Promise<{
|
|
258
|
+
notificationId: string | null;
|
|
259
|
+
}>;
|
|
260
|
+
};
|
|
261
|
+
export type PhisNotificationRecipient = {
|
|
262
|
+
kind: "user";
|
|
263
|
+
userId: number;
|
|
264
|
+
locale?: string | null;
|
|
265
|
+
} | {
|
|
266
|
+
kind: "contact";
|
|
267
|
+
address: string;
|
|
268
|
+
locale: string;
|
|
269
|
+
name?: string | null;
|
|
270
|
+
};
|
|
271
|
+
export type PhisNotificationInput = {
|
|
272
|
+
occasion: string;
|
|
273
|
+
recipient: PhisNotificationRecipient;
|
|
274
|
+
params: Readonly<Record<string, string>>;
|
|
275
|
+
/** Unique within the Site; a second call with the same key writes nothing and answers `null`. */
|
|
276
|
+
dedupeKey?: string | null;
|
|
277
|
+
};
|
|
227
278
|
/**
|
|
228
279
|
* What an Add-on may learn about a person.
|
|
229
280
|
*
|
package/dist/core.d.ts
CHANGED
|
@@ -48,6 +48,8 @@ export declare const PHIS_CORE_CAPABILITIES: {
|
|
|
48
48
|
readonly roles: "@phis/server/roles:v1";
|
|
49
49
|
readonly payments: "@phis/server/payments:v1";
|
|
50
50
|
readonly paymentNotifications: "@phis/server/payment-notifications:v1";
|
|
51
|
+
/** Writing to a person about an occasion the Add-on declared, with Core owning frame and transport. */
|
|
52
|
+
readonly notifications: "@phis/server/notifications:v1";
|
|
51
53
|
};
|
|
52
54
|
export type PhisCoreCapabilityId = (typeof PHIS_CORE_CAPABILITIES)[keyof typeof PHIS_CORE_CAPABILITIES];
|
|
53
55
|
/**
|
package/dist/core.js
CHANGED
|
@@ -47,6 +47,8 @@ export const PHIS_CORE_CAPABILITIES = {
|
|
|
47
47
|
roles: "@phis/server/roles:v1",
|
|
48
48
|
payments: "@phis/server/payments:v1",
|
|
49
49
|
paymentNotifications: "@phis/server/payment-notifications:v1",
|
|
50
|
+
/** Writing to a person about an occasion the Add-on declared, with Core owning frame and transport. */
|
|
51
|
+
notifications: "@phis/server/notifications:v1",
|
|
50
52
|
};
|
|
51
53
|
/**
|
|
52
54
|
* What Core tells an Add-on about after the fact.
|
package/dist/payments.d.ts
CHANGED
|
@@ -4,6 +4,10 @@
|
|
|
4
4
|
* Execution belongs to Core; its implemented capability, service and event identities live in core.ts.
|
|
5
5
|
* These declarations contain no SDK, credentials, persistence or exchange-rate conversion. See phis-server's
|
|
6
6
|
* PAYMENTS.md and PAYMENTS_STRIPE.md for authority, durability and Provider mapping requirements.
|
|
7
|
+
*
|
|
8
|
+
* `amount` is always the consumer's base amount. Under a pricing policy other than `final`/`none` the
|
|
9
|
+
* Provider adds tax, shipping and discounts on top of it and reports the customer's total with its parts
|
|
10
|
+
* (PAYMENTS.md section 12); authorized, captured and refunded funds are measured against that total.
|
|
7
11
|
*/
|
|
8
12
|
export type PhisPaymentId = string;
|
|
9
13
|
export type PhisPaymentOperationId = string;
|
|
@@ -23,6 +27,7 @@ export type PhisPaymentFeatures = {
|
|
|
23
27
|
canCancel: boolean;
|
|
24
28
|
canRefund: boolean;
|
|
25
29
|
canPartialRefund: boolean;
|
|
30
|
+
pricing: PhisPaymentPricingFeatures;
|
|
26
31
|
};
|
|
27
32
|
export type PhisPaymentMethod = {
|
|
28
33
|
profileKey: string;
|
|
@@ -47,7 +52,10 @@ export type PhisPayment = {
|
|
|
47
52
|
reference: string;
|
|
48
53
|
profileKey: string;
|
|
49
54
|
mode: "test" | "live";
|
|
55
|
+
/** The consumer's base amount; the customer pays `totals.total` once the Provider reports it. */
|
|
50
56
|
amount: PhisPaymentMoney;
|
|
57
|
+
pricing: PhisPaymentPricing;
|
|
58
|
+
totals: PhisPaymentTotals | null;
|
|
51
59
|
captureMode: PhisPaymentCaptureMode;
|
|
52
60
|
status: PhisPaymentStatus;
|
|
53
61
|
authorizedMinor: string;
|
|
@@ -71,6 +79,8 @@ export type PhisPaymentRefund = {
|
|
|
71
79
|
id: PhisPaymentRefundId;
|
|
72
80
|
paymentId: PhisPaymentId;
|
|
73
81
|
amount: PhisPaymentMoney;
|
|
82
|
+
/** The tax or discount share of this refund when the Provider reports one; informational. */
|
|
83
|
+
additions: readonly PhisPaymentAddition[] | null;
|
|
74
84
|
status: "pending" | "succeeded" | "failed" | "canceled";
|
|
75
85
|
};
|
|
76
86
|
export type PhisPaymentCommandResult = {
|
|
@@ -83,6 +93,7 @@ export type PhisPaymentCreateInput = {
|
|
|
83
93
|
reference: string;
|
|
84
94
|
idempotencyKey: string;
|
|
85
95
|
amount: PhisPaymentMoney;
|
|
96
|
+
pricing: PhisPaymentPricing;
|
|
86
97
|
description: string;
|
|
87
98
|
captureMode: PhisPaymentCaptureMode;
|
|
88
99
|
methodKey: string | null;
|
|
@@ -99,6 +110,7 @@ export interface PhisPaymentsCapabilityV1 {
|
|
|
99
110
|
amount: PhisPaymentMoney;
|
|
100
111
|
captureMode: PhisPaymentCaptureMode;
|
|
101
112
|
checkoutKind: PhisPaymentCheckoutKind;
|
|
113
|
+
pricing: PhisPaymentPricingSelector;
|
|
102
114
|
}): Promise<readonly PhisPaymentMethod[]>;
|
|
103
115
|
/** Core binds Site/consumer/profile; the consumer checks access to its business resource. */
|
|
104
116
|
create(input: PhisPaymentCreateInput): Promise<PhisPaymentCommandResult>;
|
|
@@ -143,7 +155,10 @@ export type PhisPaymentProviderObservation = {
|
|
|
143
155
|
merchantIdentity: string;
|
|
144
156
|
mode: "test" | "live";
|
|
145
157
|
status: PhisPaymentStatus;
|
|
158
|
+
/** The base amount as created; a Provider never changes it. */
|
|
146
159
|
amount: PhisPaymentMoney;
|
|
160
|
+
/** Null only while the Provider cannot yet know the total; required from `authorized` on. */
|
|
161
|
+
totals: PhisPaymentObservedTotals | null;
|
|
147
162
|
authorizedMinor: string;
|
|
148
163
|
capturedMinor: string;
|
|
149
164
|
authorizationExpiresAt: string | null;
|
|
@@ -154,6 +169,7 @@ export type PhisPaymentProviderObservation = {
|
|
|
154
169
|
binding: PhisPaymentProviderBinding;
|
|
155
170
|
minor: string;
|
|
156
171
|
status: PhisPaymentRefund["status"];
|
|
172
|
+
additions: readonly PhisPaymentAddition[] | null;
|
|
157
173
|
}[];
|
|
158
174
|
};
|
|
159
175
|
export type PhisPaymentProviderCommand = {
|
|
@@ -164,6 +180,7 @@ export type PhisPaymentProviderCommand = {
|
|
|
164
180
|
} & ({
|
|
165
181
|
action: "create";
|
|
166
182
|
amount: PhisPaymentMoney;
|
|
183
|
+
pricing: PhisPaymentPricing;
|
|
167
184
|
description: string;
|
|
168
185
|
captureMode: PhisPaymentCaptureMode;
|
|
169
186
|
methodKey: string | null;
|
|
@@ -211,6 +228,7 @@ export interface PhisPaymentProviderV1 {
|
|
|
211
228
|
amount: PhisPaymentMoney;
|
|
212
229
|
captureMode: PhisPaymentCaptureMode;
|
|
213
230
|
checkoutKind: PhisPaymentCheckoutKind;
|
|
231
|
+
pricing: PhisPaymentPricingSelector;
|
|
214
232
|
signal: AbortSignal;
|
|
215
233
|
}): Promise<PhisPaymentProviderProbe>;
|
|
216
234
|
execute(input: PhisPaymentProviderCommand): Promise<PhisPaymentProviderExecution>;
|
|
@@ -272,3 +290,96 @@ export type PhisPaymentChangedEventFacts = {
|
|
|
272
290
|
refundId: PhisPaymentRefundId | null;
|
|
273
291
|
};
|
|
274
292
|
};
|
|
293
|
+
export type PhisPaymentPricingBasis = "final" | "net";
|
|
294
|
+
export type PhisPaymentTaxHandling = "none" | "provider" | "merchant-of-record";
|
|
295
|
+
export type PhisPaymentPromotionCodes = "none" | "provider";
|
|
296
|
+
/** What is sold, never a rate; each Provider maps the closed vocabulary to its own codes. */
|
|
297
|
+
export type PhisPaymentTaxCategory = "general" | "reduced-goods" | "digital-goods" | "digital-service" | "service" | "shipping" | "exempt";
|
|
298
|
+
export type PhisPaymentLineItem = {
|
|
299
|
+
reference: string;
|
|
300
|
+
description: string;
|
|
301
|
+
quantity: number;
|
|
302
|
+
unitMinor: string;
|
|
303
|
+
taxCategory: PhisPaymentTaxCategory;
|
|
304
|
+
/** Provider-specific code overriding the category mapping, keyed by provider key. */
|
|
305
|
+
providerTaxCodes: Readonly<Record<string, string>> | null;
|
|
306
|
+
};
|
|
307
|
+
/** A reduction the consumer decided; the Provider shows it and taxes the reduced amount. */
|
|
308
|
+
export type PhisPaymentDiscount = {
|
|
309
|
+
reference: string;
|
|
310
|
+
label: string;
|
|
311
|
+
minor: string;
|
|
312
|
+
};
|
|
313
|
+
export type PhisPaymentCustomerDetails = {
|
|
314
|
+
country: string;
|
|
315
|
+
postalCode: string | null;
|
|
316
|
+
region: string | null;
|
|
317
|
+
addressSource: "billing" | "shipping";
|
|
318
|
+
taxIds: readonly {
|
|
319
|
+
type: string;
|
|
320
|
+
value: string;
|
|
321
|
+
}[];
|
|
322
|
+
};
|
|
323
|
+
/**
|
|
324
|
+
* `final`/`none` is v1: the base amount is what the customer pays and the Provider adds nothing. Every
|
|
325
|
+
* other policy carries line items; the base amount then equals their sum plus shipping minus discounts.
|
|
326
|
+
*/
|
|
327
|
+
export type PhisPaymentPricing = {
|
|
328
|
+
basis: "final";
|
|
329
|
+
tax: "none";
|
|
330
|
+
} | {
|
|
331
|
+
basis: PhisPaymentPricingBasis;
|
|
332
|
+
tax: PhisPaymentTaxHandling;
|
|
333
|
+
lineItems: readonly PhisPaymentLineItem[];
|
|
334
|
+
shipping: {
|
|
335
|
+
minor: string;
|
|
336
|
+
taxCategory: "shipping";
|
|
337
|
+
} | null;
|
|
338
|
+
discounts: readonly PhisPaymentDiscount[];
|
|
339
|
+
promotionCodes: PhisPaymentPromotionCodes;
|
|
340
|
+
customer: PhisPaymentCustomerDetails | null;
|
|
341
|
+
};
|
|
342
|
+
/** The facts of a policy a method must support; `discounts` is the number of stated discounts. */
|
|
343
|
+
export type PhisPaymentPricingSelector = {
|
|
344
|
+
basis: PhisPaymentPricingBasis;
|
|
345
|
+
tax: PhisPaymentTaxHandling;
|
|
346
|
+
discounts: number;
|
|
347
|
+
promotionCodes: PhisPaymentPromotionCodes;
|
|
348
|
+
};
|
|
349
|
+
export type PhisPaymentPricingFeatures = {
|
|
350
|
+
/** Always includes `final`. */
|
|
351
|
+
bases: readonly PhisPaymentPricingBasis[];
|
|
352
|
+
/** Always includes `none`. */
|
|
353
|
+
tax: readonly PhisPaymentTaxHandling[];
|
|
354
|
+
/** How many stated discounts one payment may carry; 0 when the Provider shows none. */
|
|
355
|
+
maxDiscounts: number;
|
|
356
|
+
/** Whether a customer may enter a code on the Provider's page, and whether next to stated discounts. */
|
|
357
|
+
promotionCodes: "none" | "alone" | "with-discounts";
|
|
358
|
+
collectsCustomerDetails: boolean;
|
|
359
|
+
collectsTaxIds: boolean;
|
|
360
|
+
};
|
|
361
|
+
export type PhisPaymentAdditionKind = "tax" | "shipping" | "discount";
|
|
362
|
+
export type PhisPaymentAddition = {
|
|
363
|
+
kind: PhisPaymentAdditionKind;
|
|
364
|
+
minor: string;
|
|
365
|
+
label: string;
|
|
366
|
+
/** Contained in the base amount rather than added to it; a `discount` is never inclusive. */
|
|
367
|
+
inclusive: boolean;
|
|
368
|
+
/** Display-only decimal string such as "8.1"; never enters Core arithmetic. */
|
|
369
|
+
rate: string | null;
|
|
370
|
+
/** ISO country code, optionally followed by "-" and a region. */
|
|
371
|
+
jurisdiction: string | null;
|
|
372
|
+
/** Who owes a tax addition to the authority: the merchant, or the Provider as Merchant of Record. */
|
|
373
|
+
collectedBy: "merchant" | "provider";
|
|
374
|
+
/** The promotion code or stated discount reference behind a `discount`; null for every other kind. */
|
|
375
|
+
code: string | null;
|
|
376
|
+
};
|
|
377
|
+
export type PhisPaymentObservedTotals = {
|
|
378
|
+
total: PhisPaymentMoney;
|
|
379
|
+
additions: readonly PhisPaymentAddition[];
|
|
380
|
+
customer: PhisPaymentCustomerDetails | null;
|
|
381
|
+
};
|
|
382
|
+
export type PhisPaymentTotals = PhisPaymentObservedTotals & {
|
|
383
|
+
/** Set by Core when the total became binding; a differing total afterwards is an inconsistency. */
|
|
384
|
+
frozenAt: string | null;
|
|
385
|
+
};
|
package/dist/payments.js
CHANGED
|
@@ -4,5 +4,9 @@
|
|
|
4
4
|
* Execution belongs to Core; its implemented capability, service and event identities live in core.ts.
|
|
5
5
|
* These declarations contain no SDK, credentials, persistence or exchange-rate conversion. See phis-server's
|
|
6
6
|
* PAYMENTS.md and PAYMENTS_STRIPE.md for authority, durability and Provider mapping requirements.
|
|
7
|
+
*
|
|
8
|
+
* `amount` is always the consumer's base amount. Under a pricing policy other than `final`/`none` the
|
|
9
|
+
* Provider adds tax, shipping and discounts on top of it and reports the customer's total with its parts
|
|
10
|
+
* (PAYMENTS.md section 12); authorized, captured and refunded funds are measured against that total.
|
|
7
11
|
*/
|
|
8
12
|
export {};
|