@hyperscale0/hsx 1.0.0-alpha.6 → 1.0.0-rc.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/CHANGELOG.md +48 -10
- package/LICENSING.md +3 -3
- package/README.md +37 -149
- package/TRADEMARKS.md +4 -4
- package/dist/src/ast.d.ts +129 -10
- package/dist/src/ast.d.ts.map +1 -1
- package/dist/src/cli.d.ts +2 -1
- package/dist/src/cli.d.ts.map +1 -1
- package/dist/src/cli.js +148 -12
- package/dist/src/cli.js.map +1 -1
- package/dist/src/compile.d.ts +41 -4
- package/dist/src/compile.d.ts.map +1 -1
- package/dist/src/compile.js +82 -15
- package/dist/src/compile.js.map +1 -1
- package/dist/src/cost.d.ts +78 -0
- package/dist/src/cost.d.ts.map +1 -0
- package/dist/src/cost.js +369 -0
- package/dist/src/cost.js.map +1 -0
- package/dist/src/diagnostics.d.ts +11 -0
- package/dist/src/diagnostics.d.ts.map +1 -0
- package/dist/src/diagnostics.js +420 -0
- package/dist/src/diagnostics.js.map +1 -0
- package/dist/src/emit.d.ts +35 -0
- package/dist/src/emit.d.ts.map +1 -0
- package/dist/src/emit.js +359 -0
- package/dist/src/emit.js.map +1 -0
- package/dist/src/entry-overrides.d.ts +17 -3
- package/dist/src/entry-overrides.d.ts.map +1 -1
- package/dist/src/entry-overrides.js +180 -35
- package/dist/src/entry-overrides.js.map +1 -1
- package/dist/src/format.d.ts +10 -0
- package/dist/src/format.d.ts.map +1 -0
- package/dist/src/format.js +183 -0
- package/dist/src/format.js.map +1 -0
- package/dist/src/index.d.ts +10 -4
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +8 -3
- package/dist/src/index.js.map +1 -1
- package/dist/src/ir.d.ts +66 -0
- package/dist/src/ir.d.ts.map +1 -0
- package/dist/src/ir.js +15 -0
- package/dist/src/ir.js.map +1 -0
- package/dist/src/lex.d.ts +9 -1
- package/dist/src/lex.d.ts.map +1 -1
- package/dist/src/lex.js +50 -4
- package/dist/src/lex.js.map +1 -1
- package/dist/src/limits.d.ts +2 -3
- package/dist/src/limits.d.ts.map +1 -1
- package/dist/src/limits.js +2 -3
- package/dist/src/limits.js.map +1 -1
- package/dist/src/modules.d.ts +36 -0
- package/dist/src/modules.d.ts.map +1 -0
- package/dist/src/modules.js +372 -0
- package/dist/src/modules.js.map +1 -0
- package/dist/src/parse.d.ts.map +1 -1
- package/dist/src/parse.js +590 -17
- package/dist/src/parse.js.map +1 -1
- package/dist/src/typecheck.d.ts +8 -0
- package/dist/src/typecheck.d.ts.map +1 -0
- package/dist/src/typecheck.js +2958 -0
- package/dist/src/typecheck.js.map +1 -0
- package/dist/src/version.d.ts +4 -15
- package/dist/src/version.d.ts.map +1 -1
- package/dist/src/version.js +4 -15
- package/dist/src/version.js.map +1 -1
- package/docs/README.md +23 -0
- package/docs/guide/01-first-program.md +24 -0
- package/docs/guide/02-money.md +25 -0
- package/docs/guide/03-instruments.md +22 -0
- package/docs/guide/04-lifecycles.md +25 -0
- package/docs/guide/05-fees-and-splits.md +45 -0
- package/docs/guide/06-schedules.md +22 -0
- package/docs/guide/07-composition.md +46 -0
- package/docs/guide/08-writing-a-module.md +26 -0
- package/docs/guide/09-cost.md +24 -0
- package/docs/guide/10-diagnostics.md +27 -0
- package/docs/llms-full.txt +1738 -0
- package/docs/llms.txt +45 -0
- package/docs/reference/cli.md +33 -0
- package/docs/reference/diagnostics.md +619 -0
- package/docs/reference/grammar.md +39 -0
- package/docs/reference/std/advance.md +35 -0
- package/docs/reference/std/cancellable_booking.md +35 -0
- package/docs/reference/std/captured_payment.md +43 -0
- package/docs/reference/std/conditional_disbursement.md +33 -0
- package/docs/reference/std/credit_facility.md +35 -0
- package/docs/reference/std/held_payment.md +64 -0
- package/docs/reference/std/instant_transfer.md +30 -0
- package/docs/reference/std/metered.md +27 -0
- package/docs/reference/std/pooled_split.md +26 -0
- package/docs/reference/std/premium_forward.md +39 -0
- package/docs/reference/std/reconciled_payout.md +33 -0
- package/docs/reference/std/recurring_collection.md +25 -0
- package/docs/reference/std/rotating_pool.md +42 -0
- package/docs/reference/std/scheduled.md +51 -0
- package/docs/reference/std/security_deposit.md +46 -0
- package/docs/reference/std/settlement_batch.md +42 -0
- package/docs/reference/std/swap.md +46 -0
- package/docs/reference/std/threshold_pool.md +47 -0
- package/docs/reference/std/weighted_distribution.md +39 -0
- package/docs/reference/types.md +20 -0
- package/docs/reference/udl-output.md +14 -0
- package/docs/sessions/2026-09-02-two-instruments.hsx +21 -0
- package/docs/sessions/2026-09-02-two-instruments.md +41 -0
- package/examples/01-first-program/README.md +45 -0
- package/examples/01-first-program/tip-jar.hsx +17 -0
- package/examples/02-imports-and-archetypes/README.md +99 -0
- package/examples/02-imports-and-archetypes/photo-booth.hsx +37 -0
- package/examples/03-diagnostics/README.md +68 -0
- package/examples/03-diagnostics/corner-shop-fixed.hsx +22 -0
- package/examples/03-diagnostics/corner-shop.hsx +19 -0
- package/examples/04-complete-product/README.md +87 -0
- package/examples/04-complete-product/study-hall.hsx +67 -0
- package/examples/05-watch-club/README.md +9 -0
- package/examples/05-watch-club/watch-club.hsx +124 -0
- package/examples/README.md +18 -0
- package/examples/advance/README.md +3 -0
- package/examples/advance/advance.hsx +13 -0
- package/examples/advance/advance.udl +339 -0
- package/examples/cancellable_booking/README.md +3 -0
- package/examples/cancellable_booking/cancellable_booking.hsx +14 -0
- package/examples/cancellable_booking/cancellable_booking.udl +391 -0
- package/examples/captured_payment/README.md +3 -0
- package/examples/captured_payment/captured_payment.hsx +21 -0
- package/examples/captured_payment/captured_payment.udl +677 -0
- package/examples/conditional_disbursement/README.md +3 -0
- package/examples/conditional_disbursement/conditional_disbursement.hsx +17 -0
- package/examples/conditional_disbursement/conditional_disbursement.udl +412 -0
- package/examples/cost-table.json +386 -0
- package/examples/credit_facility/README.md +3 -0
- package/examples/credit_facility/credit_facility.hsx +27 -0
- package/examples/credit_facility/credit_facility.udl +1373 -0
- package/examples/held_payment/README.md +3 -0
- package/examples/held_payment/held_payment.hsx +13 -0
- package/examples/held_payment/held_payment.udl +507 -0
- package/examples/instant_transfer/README.md +3 -0
- package/examples/instant_transfer/instant_transfer.hsx +16 -0
- package/examples/instant_transfer/instant_transfer.udl +364 -0
- package/examples/metered/README.md +3 -0
- package/examples/metered/metered.hsx +13 -0
- package/examples/metered/metered.udl +247 -0
- package/examples/pooled_split/README.md +3 -0
- package/examples/pooled_split/pooled_split.hsx +15 -0
- package/examples/pooled_split/pooled_split.udl +425 -0
- package/examples/premium_forward/README.md +3 -0
- package/examples/premium_forward/premium_forward.hsx +19 -0
- package/examples/premium_forward/premium_forward.udl +594 -0
- package/examples/reconciled_payout/README.md +3 -0
- package/examples/reconciled_payout/reconciled_payout.hsx +13 -0
- package/examples/reconciled_payout/reconciled_payout.udl +303 -0
- package/examples/recurring_collection/README.md +3 -0
- package/examples/recurring_collection/recurring_collection.hsx +21 -0
- package/examples/recurring_collection/recurring_collection.udl +1130 -0
- package/examples/rotating_pool/README.md +3 -0
- package/examples/rotating_pool/rotating_pool.hsx +18 -0
- package/examples/rotating_pool/rotating_pool.udl +4352 -0
- package/examples/scheduled/README.md +3 -0
- package/examples/scheduled/scheduled.hsx +12 -0
- package/examples/scheduled/scheduled.udl +335 -0
- package/examples/security_deposit/README.md +3 -0
- package/examples/security_deposit/security_deposit.hsx +21 -0
- package/examples/security_deposit/security_deposit.udl +293 -0
- package/examples/settlement_batch/README.md +3 -0
- package/examples/settlement_batch/settlement_batch.hsx +19 -0
- package/examples/settlement_batch/settlement_batch.udl +753 -0
- package/examples/swap/README.md +3 -0
- package/examples/swap/swap.hsx +19 -0
- package/examples/swap/swap.udl +876 -0
- package/examples/threshold_pool/README.md +3 -0
- package/examples/threshold_pool/threshold_pool.hsx +16 -0
- package/examples/threshold_pool/threshold_pool.udl +667 -0
- package/examples/weighted_distribution/README.md +3 -0
- package/examples/weighted_distribution/weighted_distribution.hsx +20 -0
- package/examples/weighted_distribution/weighted_distribution.udl +337 -0
- package/package.json +19 -6
- package/skills/hsx/SKILL.md +483 -0
- package/src/ast.ts +163 -9
- package/src/cli.ts +164 -12
- package/src/compile.ts +160 -20
- package/src/cost.ts +579 -0
- package/src/diagnostics.ts +442 -0
- package/src/emit.ts +447 -0
- package/src/entry-overrides.ts +263 -48
- package/src/format.ts +261 -0
- package/src/index.ts +32 -4
- package/src/ir.ts +99 -0
- package/src/lex.ts +59 -4
- package/src/limits.ts +2 -3
- package/src/modules.ts +444 -0
- package/src/parse.ts +655 -21
- package/src/typecheck.ts +3876 -0
- package/src/version.ts +4 -16
- package/std/SEMANTICS.md +130 -0
- package/std/settlements/advance.hsx +177 -0
- package/std/settlements/cancellable_booking.hsx +87 -0
- package/std/settlements/captured_payment.hsx +144 -0
- package/std/settlements/conditional_disbursement.hsx +185 -0
- package/std/settlements/credit_facility.hsx +167 -0
- package/std/settlements/held_payment.hsx +578 -0
- package/std/settlements/index.hsx +3 -0
- package/std/settlements/instant_transfer.hsx +190 -0
- package/std/settlements/metered.hsx +67 -0
- package/std/settlements/pooled_split.hsx +68 -0
- package/std/settlements/premium_forward.hsx +257 -0
- package/std/settlements/reconciled_payout.hsx +77 -0
- package/std/settlements/recurring_collection.hsx +27 -0
- package/std/settlements/rotating_pool.hsx +252 -0
- package/std/settlements/scheduled.hsx +641 -0
- package/std/settlements/security_deposit.hsx +166 -0
- package/std/settlements/settlement_batch.hsx +122 -0
- package/std/settlements/swap.hsx +1114 -0
- package/std/settlements/threshold_pool.hsx +221 -0
- package/std/settlements/weighted_distribution.hsx +141 -0
- package/dist/src/archetypes.d.ts +0 -17
- package/dist/src/archetypes.d.ts.map +0 -1
- package/dist/src/archetypes.js +0 -327
- package/dist/src/archetypes.js.map +0 -1
- package/dist/src/check.d.ts +0 -12
- package/dist/src/check.d.ts.map +0 -1
- package/dist/src/check.js +0 -1910
- package/dist/src/check.js.map +0 -1
- package/dist/src/lower.d.ts +0 -146
- package/dist/src/lower.d.ts.map +0 -1
- package/dist/src/lower.js +0 -4570
- package/dist/src/lower.js.map +0 -1
- package/dist/src/model.d.ts +0 -467
- package/dist/src/model.d.ts.map +0 -1
- package/dist/src/model.js +0 -15
- package/dist/src/model.js.map +0 -1
- package/spec/hsx-ir.schema.json +0 -920
- package/src/archetypes.ts +0 -340
- package/src/check.ts +0 -2938
- package/src/lower.ts +0 -5416
- package/src/model.ts +0 -519
package/src/version.ts
CHANGED
|
@@ -1,17 +1,5 @@
|
|
|
1
|
-
/**
|
|
2
|
-
|
|
3
|
-
*
|
|
4
|
-
* `HSX_VERSION` is the npm package's semver: it moves when this compiler's
|
|
5
|
-
* code changes, including changes that only affect diagnostics or docs.
|
|
6
|
-
*
|
|
7
|
-
* `HSX_IR_VERSION` is the format number stamped into every compiled document
|
|
8
|
-
* as its `hsx` field. It moves only when the emitted IR shape changes in a
|
|
9
|
-
* way a consumer must notice, so a runtime can refuse a document it does not
|
|
10
|
-
* understand by reading one integer. A package release almost never bumps it.
|
|
11
|
-
*/
|
|
1
|
+
/** Package version reported by the command line. */
|
|
2
|
+
export const HSX_VERSION = "1.0.0-rc.1";
|
|
12
3
|
|
|
13
|
-
/**
|
|
14
|
-
export const
|
|
15
|
-
|
|
16
|
-
/** Kept equal to the package.json version by `test/spec.spec.ts`. */
|
|
17
|
-
export const HSX_VERSION = "1.0.0-alpha.6";
|
|
4
|
+
/** Canonical UDL contract version emitted by this compiler. */
|
|
5
|
+
export const HSX_TARGET_UDL_VERSION = 1;
|
package/std/SEMANTICS.md
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
# Settlement semantics frozen before legacy teardown
|
|
2
|
+
|
|
3
|
+
This note records the behavior that the former archetype branches emitted on
|
|
4
|
+
2026-09-01. The 48 canonical documents under
|
|
5
|
+
`test/fixtures/general-path-oracle/` are the byte-level authority. This note is
|
|
6
|
+
the human porting index.
|
|
7
|
+
|
|
8
|
+
## Direct and held payments
|
|
9
|
+
|
|
10
|
+
- `instant_transfer` stores the amount, currency, payer, and payee. Create pays
|
|
11
|
+
through one or more ordered pieces and ends in `paid`. A payer fee is an
|
|
12
|
+
`on_top` move. A payee fee is carved from the base amount. Exact, basis-point,
|
|
13
|
+
and tiered rules produce fee fields and `feeRules`. The finest common
|
|
14
|
+
refinement of all fee cuts produces piece fields and one partition. Floor
|
|
15
|
+
rounding sends the remainder to the named non-fee recipient. A derived
|
|
16
|
+
amount adds a floor percentage field, `derivedAmounts`, a platform party, and
|
|
17
|
+
its own transfer piece.
|
|
18
|
+
- `held_payment` reserves every refined piece before release. It posts release
|
|
19
|
+
pieces, voids cancellation pieces, and keeps payer service fees outside the
|
|
20
|
+
held principal. Release and cancellation fee sides share the finest common
|
|
21
|
+
partition, so each stored piece has one release recipient and one cancel
|
|
22
|
+
recipient. States track each funding, release, cancellation, and abandonment
|
|
23
|
+
step. Abandonment refunds every still-held piece. A deadline release and a
|
|
24
|
+
caller decision release carry the named date or port clause. Retention is the
|
|
25
|
+
same form with one held piece, a release deadline, and cancellation back to
|
|
26
|
+
the contractor. Whole-amount mode funds the principal and on-top fee in one
|
|
27
|
+
action, then releases or cancels the principal whole. A condition and date
|
|
28
|
+
can coexist, with separate early and scheduled release actions.
|
|
29
|
+
- `captured_payment` stores authorization total, reserve and reversal dates,
|
|
30
|
+
and both party accounts. It moves through `created`, `authorized`, optional
|
|
31
|
+
`partially_captured`, `settled`, `voided`, `expired`, `corrected`, or
|
|
32
|
+
`reversed`. Authorize reserves custody. Capture and capture-more consume the
|
|
33
|
+
authorized balance according to the declared mode. Settle posts the captured
|
|
34
|
+
amount. Void and expiry release the reserve. Correction and external reversal
|
|
35
|
+
use their declared ports and windows. Derived fees use the same floor,
|
|
36
|
+
partition, bearer, and position rules as direct payment.
|
|
37
|
+
- `premium_forward` holds premium pieces, then forwards the net pieces after a
|
|
38
|
+
bind decision. It can abandon unbound custody. Policy reference, renewal due,
|
|
39
|
+
endorsement evidence, and lapse actions appear only when declared. Its fee
|
|
40
|
+
partition and floor remainder rules match `held_payment`.
|
|
41
|
+
|
|
42
|
+
## Decisions and credit
|
|
43
|
+
|
|
44
|
+
- `conditional_disbursement` emits a submitted parent with cap, currency,
|
|
45
|
+
source, destination, and the decision port. Denial terminates the parent. An
|
|
46
|
+
approved-amount child stores a runtime-bounded amount and a parent reference.
|
|
47
|
+
The child captures the port input, moves through `created`, `approved`, and
|
|
48
|
+
`paid`, and refuses reopening. Recovery is a separate transfer.
|
|
49
|
+
- `advance` has two forms. A carved advance references a held settlement and
|
|
50
|
+
its recourse settlement, disburses once, and settles from the referenced
|
|
51
|
+
release without minting new value. A scheduled advance stores advance,
|
|
52
|
+
fee, repayable total, first due date, and one repayment field per fixed
|
|
53
|
+
installment. It partitions repayments to the repayable total and advances
|
|
54
|
+
before collecting each due installment.
|
|
55
|
+
- `credit_facility` stores lender, borrower, draw destination, limit, currency,
|
|
56
|
+
and expiry. The facility can freeze and close. Each draw is a generated child
|
|
57
|
+
with amount, facility reference, and obligation reference. Draw admission
|
|
58
|
+
checks the facility state and aggregate limit. Resolution waits for the
|
|
59
|
+
referenced obligation state.
|
|
60
|
+
|
|
61
|
+
## Schedules and usage
|
|
62
|
+
|
|
63
|
+
- A finite `scheduled` transfer stores total, first due date, and one money
|
|
64
|
+
field per installment. Floor division creates equal pieces and gives the
|
|
65
|
+
final field the remainder. States and actions unroll in order. Each action
|
|
66
|
+
carries its due rule and cumulative duration offset.
|
|
67
|
+
- Scheduled obligation mode emits a parent plus one generated payment child per
|
|
68
|
+
installment. The parent stores principal, delinquency dates, party accounts,
|
|
69
|
+
partitions, and aggregate invariants. It supports draft approval, optional
|
|
70
|
+
advance, fixed collection and delinquency actions, write-off, and completion.
|
|
71
|
+
Each child binds parent fields, repays once, and can refund once.
|
|
72
|
+
- `recurring_collection` with a finite count uses the scheduled construction.
|
|
73
|
+
Open recurrence stores one amount and anchor, opens one period at a time,
|
|
74
|
+
collects that period, and permits cancellation. It never unrolls an unbounded
|
|
75
|
+
runtime loop.
|
|
76
|
+
- `metered` stores one money field for each declared meter and a period end.
|
|
77
|
+
Every charge moves directly from payer to payee. The instrument never accrues
|
|
78
|
+
custody. Closing the period only changes state.
|
|
79
|
+
|
|
80
|
+
## Collections and distributions
|
|
81
|
+
|
|
82
|
+
- `pooled_split` stores the pool total, due date, and one share amount and
|
|
83
|
+
account per recipient. Positive shares total exactly 10,000 basis points.
|
|
84
|
+
Floor division assigns each share, and the named remainder recipient absorbs
|
|
85
|
+
minor-unit residue. Funding and distribution actions unroll in roster order.
|
|
86
|
+
- `weighted_distribution` emits an open parent and an entitlement child. The
|
|
87
|
+
parent stores source, total, record date, and maximum recipient count. A port
|
|
88
|
+
freezes the entitlement snapshot. Child rows store recipient, weight, source,
|
|
89
|
+
currency, and parent reference. Largest-remainder distribution pays children
|
|
90
|
+
once. Aggregate clauses cap and total the child set. Withholding is refused
|
|
91
|
+
and correction creates a new distribution. Its `flat` form emits a single
|
|
92
|
+
claim instead. The caller supplies typed pool, weight, and group references,
|
|
93
|
+
their state gates, copy and match maps, and the pool amount path.
|
|
94
|
+
- `threshold_pool` emits a pool parent and commitment child. The parent stores
|
|
95
|
+
target, close date, maximum contributors, beneficiary, and currency. It moves
|
|
96
|
+
through open, active, failed, or settled based on aggregate commitment gates.
|
|
97
|
+
Each commitment can cancel before collection, collect into the round, or
|
|
98
|
+
refund after failure. Its referenced-contribution form accepts a published
|
|
99
|
+
contribution instrument, beneficiary account, and memo and emits no child.
|
|
100
|
+
- `security_deposit` reserves the full amount. A claim port posts either the
|
|
101
|
+
whole hold or a decided amount bounded by the hold. The explicit remainder
|
|
102
|
+
returns to the payer. A return port voids the whole hold. The deadline form
|
|
103
|
+
adds machine expiry and unfunded cancellation. Claim and return targets can
|
|
104
|
+
name either party.
|
|
105
|
+
|
|
106
|
+
## Composition and unwind
|
|
107
|
+
|
|
108
|
+
- `swap` stores both side amounts, accounts, fees, currency, and optional
|
|
109
|
+
clawback date. Funding is atomic across both sides. Release, settlement,
|
|
110
|
+
cancellation, dispute, abandonment, and clawback preserve the two-sided
|
|
111
|
+
conservation group. Side fees keep their declared bearer and position.
|
|
112
|
+
`distinctParties` prevents self-dealing. Unwind refunds each side and applies
|
|
113
|
+
the declared penalty tiers without changing the principal partition. Fixed
|
|
114
|
+
side names, state order, action bindings, parked-state reasons, and ID prefix
|
|
115
|
+
are general compile-time parameters.
|
|
116
|
+
- `settlement_batch` emits a batch plus capture, credit-adjustment, and
|
|
117
|
+
debit-adjustment children. The batch stores close time, settlement and payout
|
|
118
|
+
accounts, beneficiary reference, and currency. Close freezes intake.
|
|
119
|
+
Calculate signs capture, fee, and reversal entries. Approve rejects a negative
|
|
120
|
+
position when configured. Instruct, acknowledge, and reconcile use the
|
|
121
|
+
declared payout port. Child references and statuses define every aggregate
|
|
122
|
+
and signed-sum input.
|
|
123
|
+
- `rotating_pool` emits one parent plus one contribution child per fixed member.
|
|
124
|
+
The parent unrolls active and ready states for every cycle and checks that each
|
|
125
|
+
member row exists exactly once. Every child unrolls due, funded, defaulted,
|
|
126
|
+
guaranteed, paid, and completed states for every cycle. Due offsets scale the
|
|
127
|
+
fixed recurrence. A funded or guaranteed contribution pays the roster's fixed
|
|
128
|
+
beneficiary for that cycle. The final close requires the escrow account to be
|
|
129
|
+
drained. Its referenced-membership form delegates those member cycles to a
|
|
130
|
+
published membership instrument and emits only the parent.
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
module std.settlements.advance
|
|
2
|
+
|
|
3
|
+
export instrument advance<C>(
|
|
4
|
+
funder: party,
|
|
5
|
+
to: party,
|
|
6
|
+
amount: money<C>,
|
|
7
|
+
fee: optional<percent>,
|
|
8
|
+
count: optional<integer>,
|
|
9
|
+
every: optional<text>,
|
|
10
|
+
first_due: optional<date>,
|
|
11
|
+
against: optional<ref>,
|
|
12
|
+
) {
|
|
13
|
+
let(amount_path): concat("fields.", amount);
|
|
14
|
+
let(currency_path): "fields.currency";
|
|
15
|
+
let(funder_field): camel(concat(funder, "_account_id"));
|
|
16
|
+
let(to_field): camel(concat(to, "_account_id"));
|
|
17
|
+
|
|
18
|
+
agent_description: "Reach for advance when the {funder} pays the {to} up front and is repaid afterwards, either out of a carved hold release or over scheduled repayments. Pick deposit for a refundable reservation and instant transfer when nobody repays the payer.";
|
|
19
|
+
title: sentence(instrument);
|
|
20
|
+
id_prefix: prefix(instrument);
|
|
21
|
+
parties {
|
|
22
|
+
payer: funder;
|
|
23
|
+
beneficiary: to;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
when(against) {
|
|
27
|
+
let(hold_name): owner(against);
|
|
28
|
+
summary: "Advance to the {to} repaid by carving the {hold_name} release";
|
|
29
|
+
description: "Advance: the {funder} disburses {amount} to the {to} and is repaid out of the {hold_name} release, which pays the {funder} in the {to}'s place";
|
|
30
|
+
fields {
|
|
31
|
+
amount {
|
|
32
|
+
type: money<C>;
|
|
33
|
+
description: "The advanced amount in {C} minor units, disbursed to the {to} up front";
|
|
34
|
+
}
|
|
35
|
+
carve_hold_id {
|
|
36
|
+
type: ref<hold_name>;
|
|
37
|
+
description: "The {hold_name} bound to this advance";
|
|
38
|
+
pattern: concat("^", prefix(hold_name), "_(sandbox|live)_[a-z0-9]{8,64}$");
|
|
39
|
+
}
|
|
40
|
+
carve_recourse_1_id {
|
|
41
|
+
type: ref<recourse>;
|
|
42
|
+
description: "The recourse bound to this advance";
|
|
43
|
+
pattern: "^reco_(sandbox|live)_[a-z0-9]{8,64}$";
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
required: [to_field, funder_field, amount, carveHoldId, carveRecourse1Id, currency];
|
|
47
|
+
lifecycle {
|
|
48
|
+
states created advanced repaid;
|
|
49
|
+
initial created;
|
|
50
|
+
on disburse: created -> advanced;
|
|
51
|
+
on settle: advanced -> repaid;
|
|
52
|
+
}
|
|
53
|
+
action create {
|
|
54
|
+
agent_description: "Open the advance row before any money moves. The caller supplies the {amount}, both party accounts, and the ids of the {hold_name} and the recourse that back repayment. Nothing is disbursed here.";
|
|
55
|
+
summary: concat("Create a ", words(instrument));
|
|
56
|
+
steps: [];
|
|
57
|
+
moves: [];
|
|
58
|
+
}
|
|
59
|
+
action disburse {
|
|
60
|
+
agent_description: "Pay the {amount} from the {funder} to the {to}. The advance must be created, the bound {hold_name} funded, and the recourse active, both matching this amount and currency. The transfer posts straight out and does not unwind.";
|
|
61
|
+
summary: "Disburse the {amount} to the {to}";
|
|
62
|
+
requires refs: [{
|
|
63
|
+
field: carveHoldId;
|
|
64
|
+
match: {
|
|
65
|
+
[currency_path]: currency_path;
|
|
66
|
+
[amount_path]: amount_path;
|
|
67
|
+
};
|
|
68
|
+
statuses: [funded];
|
|
69
|
+
}, {
|
|
70
|
+
field: carveRecourse1Id;
|
|
71
|
+
match: {
|
|
72
|
+
[currency_path]: currency_path;
|
|
73
|
+
[amount_path]: amount_path;
|
|
74
|
+
};
|
|
75
|
+
statuses: [active];
|
|
76
|
+
}];
|
|
77
|
+
moves: [{ amount: amount; from: payer; to: beneficiary; key: transfer; operation: create; }];
|
|
78
|
+
steps: [];
|
|
79
|
+
}
|
|
80
|
+
action settle {
|
|
81
|
+
agent_description: "Close the advance once the {hold_name} has released to the {funder} in the {to}'s place. The advance must already be disbursed. No money moves here, the release did the repaying.";
|
|
82
|
+
summary: concat("Close the advance once the ", hold_name, " has released to the ", funder);
|
|
83
|
+
steps: [];
|
|
84
|
+
moves: [];
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
when_not(against) {
|
|
89
|
+
let(repayment_fields): names("repayment", count, "Amount");
|
|
90
|
+
let(first_target): if_eq(count, 1, "repaid", "repaying_1");
|
|
91
|
+
let(states_key): "states";
|
|
92
|
+
let(first_due_field): camel(first_due);
|
|
93
|
+
summary: concat("Advance to ", to, " repaid over ", count, " anchors");
|
|
94
|
+
description: concat("Advance: the ", funder, " disburses ", amount, " to the ", to, ", repaid over ", count, " scheduled repayments plus the funder's discount");
|
|
95
|
+
fields {
|
|
96
|
+
amount {
|
|
97
|
+
type: money<C>;
|
|
98
|
+
description: "The advanced amount in {C} minor units, disbursed to the {to} up front";
|
|
99
|
+
}
|
|
100
|
+
fee_amount {
|
|
101
|
+
type: money<C>;
|
|
102
|
+
description: concat(fee, " of ", amount, ", the funder's discount repaid on top of the advance");
|
|
103
|
+
}
|
|
104
|
+
repayable_amount {
|
|
105
|
+
type: money<C>;
|
|
106
|
+
description: concat(amount, " + feeAmount: the total the repayment fields below partition exactly");
|
|
107
|
+
}
|
|
108
|
+
[first_due_field] {
|
|
109
|
+
type: date;
|
|
110
|
+
description: concat("Due date of the first repayment; repayment k falls ", every, " after its predecessor");
|
|
111
|
+
}
|
|
112
|
+
repayment_1_amount {
|
|
113
|
+
type: money<C>;
|
|
114
|
+
description: concat("Repayment 1 of ", count, " (carries the integer-division remainder): about 33.34% of repayableAmount, collected on its own stored-date anchor");
|
|
115
|
+
}
|
|
116
|
+
for repayment in sub(count, 1) {
|
|
117
|
+
let(index): add(repayment, 1);
|
|
118
|
+
repayment[index]Amount {
|
|
119
|
+
type: money<C>;
|
|
120
|
+
description: concat("Repayment ", index, " of ", count, ": about 33.33% of repayableAmount, collected on its own stored-date anchor");
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
lifecycle {
|
|
125
|
+
states created advanced;
|
|
126
|
+
[states_key]: [first_target];
|
|
127
|
+
initial created;
|
|
128
|
+
on disburse: created -> advanced;
|
|
129
|
+
on collect_repayment_1: advanced -> [first_target];
|
|
130
|
+
for repayment in sub(count, 1) {
|
|
131
|
+
let(index): add(repayment, 1);
|
|
132
|
+
let(previous): concat("repaying_", repayment);
|
|
133
|
+
let(target): if_eq(index, count, "repaid", concat("repaying_", index));
|
|
134
|
+
[states_key]: [target];
|
|
135
|
+
on collect_repayment_[index]: [previous] -> [target];
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
partitions: [{
|
|
139
|
+
piece_fields: repayment_fields;
|
|
140
|
+
total_field: repayableAmount;
|
|
141
|
+
}, {
|
|
142
|
+
piece_fields: [amount, feeAmount];
|
|
143
|
+
total_field: repayableAmount;
|
|
144
|
+
}];
|
|
145
|
+
action create {
|
|
146
|
+
agent_description: "Open the advance row before any money moves. The caller supplies the {amount}, the fee amount, the repayable total, and the first repayment date. The repayment fields must partition the repayable total exactly. Nothing is disbursed here.";
|
|
147
|
+
summary: concat("Create a ", words(instrument));
|
|
148
|
+
steps: [];
|
|
149
|
+
moves: [];
|
|
150
|
+
}
|
|
151
|
+
action disburse {
|
|
152
|
+
agent_description: "Pay the {amount} from the {funder} to the {to} and start the repayment schedule. The advance must be created. The transfer posts straight out and does not unwind. Each repayment is collected on its own stored date anchor.";
|
|
153
|
+
summary: "Disburse the {amount} to the {to}";
|
|
154
|
+
moves: [{ amount: amount; from: payer; to: beneficiary; key: transfer; operation: create; }];
|
|
155
|
+
steps: [];
|
|
156
|
+
}
|
|
157
|
+
action collect_repayment_1 {
|
|
158
|
+
summary: concat("Collect repayment 1 of ", count);
|
|
159
|
+
due: { field: first_due_field; };
|
|
160
|
+
moves: [{ amount: repayment1Amount; from: beneficiary; to: payer; key: transfer; operation: create; }];
|
|
161
|
+
steps: [];
|
|
162
|
+
}
|
|
163
|
+
for repayment in sub(count, 1) {
|
|
164
|
+
let(index): add(repayment, 1);
|
|
165
|
+
let(repayment_field): concat("repayment", index, "Amount");
|
|
166
|
+
action collect_repayment_[index] {
|
|
167
|
+
summary: concat("Collect repayment ", index, " of ", count);
|
|
168
|
+
due: {
|
|
169
|
+
field: first_due_field;
|
|
170
|
+
offset: scale_duration(every, sub(index, 1));
|
|
171
|
+
};
|
|
172
|
+
moves: [{ amount: repayment_field; from: beneficiary; to: payer; key: transfer; operation: create; }];
|
|
173
|
+
steps: [];
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
module std.settlements.cancellable_booking
|
|
2
|
+
|
|
3
|
+
export instrument cancellable_booking<C>(
|
|
4
|
+
guest: party,
|
|
5
|
+
host: party,
|
|
6
|
+
amount: money<C>,
|
|
7
|
+
starts_at: date,
|
|
8
|
+
late_penalty_bps: integer,
|
|
9
|
+
late_within: text,
|
|
10
|
+
early_penalty_bps: integer,
|
|
11
|
+
offer_life: text,
|
|
12
|
+
) {
|
|
13
|
+
let(guest_account_field): camel(concat(guest, "_account_id"));
|
|
14
|
+
let(host_account_field): camel(concat(host, "_account_id"));
|
|
15
|
+
let(reference_filter_key): "x-hyperscale-reference-filter";
|
|
16
|
+
let(owner_id_key): "owner.id";
|
|
17
|
+
let(owner_type_key): "owner.type";
|
|
18
|
+
fields {
|
|
19
|
+
amount { type: money<C>; description: concat("Booking price in minor units, held away from both the ", words(guest), " and the ", words(host), " until the booking ends"); }
|
|
20
|
+
currency { type: text; description: "ISO 4217 currency code"; minLength: 3; maxLength: 3; pattern: "^[A-Z]{3}$"; }
|
|
21
|
+
starts_at { type: date; description: "When the booking starts; the cancellation penalty is priced against the time left before it"; }
|
|
22
|
+
[guest_account_field] { type: account<C>; description: concat("The ", words(guest), " account the booking is taken from and the refund returns to"); pattern: "^acct_(sandbox|live)_[a-z0-9]{8,64}$"; [reference_filter_key]: { column: role; values: [customer_balance]; }; }
|
|
23
|
+
[host_account_field] { type: account<C>; description: concat("The ", words(host), " account the booking and any penalty end up in"); pattern: "^acct_(sandbox|live)_[a-z0-9]{8,64}$"; [reference_filter_key]: { column: role; values: [customer_balance]; }; }
|
|
24
|
+
}
|
|
25
|
+
lifecycle {
|
|
26
|
+
states created held cancellation_quoted canceled settled completed;
|
|
27
|
+
initial created;
|
|
28
|
+
on take: created -> held;
|
|
29
|
+
on complete: held | cancellation_quoted -> completed;
|
|
30
|
+
on cancel: held -> cancellation_quoted;
|
|
31
|
+
on confirm: cancellation_quoted -> canceled;
|
|
32
|
+
on retain: canceled -> settled;
|
|
33
|
+
}
|
|
34
|
+
parties { beneficiary: host_account_field; payer: guest_account_field; }
|
|
35
|
+
required: [amount, currency, guest_account_field, host_account_field, starts_at];
|
|
36
|
+
title: sentence(instrument);
|
|
37
|
+
agent_description: concat("Reach for cancellable booking when the ", words(guest), " pays up front and can back out at a price they see before they agree to it. The money sits in a holding account, the penalty is priced against the time left before the start, and the quote is frozen until it expires. Pick held payment when there is no cancellation price to quote.");
|
|
38
|
+
id_prefix: prefix(instrument);
|
|
39
|
+
summary: concat("Booking the ", words(guest), " can cancel at a price they see before they agree to it");
|
|
40
|
+
description: "A booking whose cancellation is quoted rather than argued: the penalty is priced against the time left before the start, frozen with the terms it was read from, and paid only if the guest confirms while the offer is still alive";
|
|
41
|
+
action create {
|
|
42
|
+
agent_description: concat("Open the booking and provision the holding account the price sits in. No money moves. The price, the currency, the start moment, and both party accounts are fixed here. Call take next to collect from the ", words(guest), ".");
|
|
43
|
+
summary: concat("Open a ", words(instrument), " and its holding account");
|
|
44
|
+
moves: [];
|
|
45
|
+
steps: [{ operation: "account.escrow.provision"; bind: { currency: { from: instance; path: fields.currency; }; [owner_id_key]: { from: instance; path: productId; }; [owner_type_key]: { from: const; value: product; }; productId: { from: instance; path: productId; }; role: { from: const; value: product_escrow; }; }; capture: { bookingHoldingAccountId: accountId; }; }];
|
|
46
|
+
}
|
|
47
|
+
action take {
|
|
48
|
+
agent_description: concat("Move the whole booking price out of the ", words(guest), " account into the holding account. This moves money. Only a created booking takes. Neither party can spend the money until the booking completes or a cancellation is confirmed.");
|
|
49
|
+
summary: concat("Take the booking price from the ", words(guest), " into the holding account");
|
|
50
|
+
moves: [{ key: booking; operation: internal_transfer.create; bind: { amount: { from: instance; path: concat("fields.", amount); }; currency: { from: instance; path: fields.currency; }; destinationAccountId: { from: instance; path: refs.bookingHoldingAccountId; }; productId: { from: instance; path: productId; }; sourceAccountId: { from: instance; path: concat("fields.", guest_account_field); }; }; capture: { bookingTransferId: transferId; }; }];
|
|
51
|
+
steps: [];
|
|
52
|
+
}
|
|
53
|
+
action complete {
|
|
54
|
+
summary: concat("Release the whole booking to the ", words(host), " once the stay has started, even with a cancellation quote parked un-acted");
|
|
55
|
+
due { field: starts_at; }
|
|
56
|
+
moves: [{ key: release; operation: internal_transfer.create; bind: { amount: { from: instance; path: concat("fields.", amount); }; currency: { from: instance; path: fields.currency; }; destinationAccountId: { from: instance; path: concat("fields.", host_account_field); }; productId: { from: instance; path: productId; }; sourceAccountId: { from: instance; path: refs.bookingHoldingAccountId; }; }; capture: { releaseTransferId: transferId; }; }];
|
|
57
|
+
steps: [];
|
|
58
|
+
}
|
|
59
|
+
action cancel {
|
|
60
|
+
agent_description: "Price the cancellation and freeze that price. No money moves. The penalty is computed from the time left before the start, and the quote expires on its own, after which a new cancel prices it again. Call confirm to spend the quote.";
|
|
61
|
+
summary: "Price the cancellation and hold that price open";
|
|
62
|
+
quote {
|
|
63
|
+
anchorField: starts_at;
|
|
64
|
+
baseField: amount;
|
|
65
|
+
chargeRef: cancellationPenaltyAmount;
|
|
66
|
+
charges: [{ bps: late_penalty_bps; withinOffset: late_within; }, { bps: early_penalty_bps; }];
|
|
67
|
+
expires { offset: offer_life; }
|
|
68
|
+
fixes: [amount, guest_account_field];
|
|
69
|
+
netDestinationField: guest_account_field;
|
|
70
|
+
netRef: cancellationRefundAmount;
|
|
71
|
+
}
|
|
72
|
+
moves: []; steps: [];
|
|
73
|
+
}
|
|
74
|
+
action confirm {
|
|
75
|
+
agent_description: concat("Spend the frozen quote and return the net refund to the ", words(guest), ". This moves money and does not reverse. It requires an un-expired quote from cancel, and the penalty the refund left behind is paid to the ", words(host), " by retain.");
|
|
76
|
+
summary: concat("Spend the quoted cancellation and return the net to the ", words(guest));
|
|
77
|
+
commit: cancel;
|
|
78
|
+
moves: [{ key: refund; operation: internal_transfer.create; bind: { amount: { from: instance; path: refs.cancellationRefundAmount; }; currency: { from: instance; path: fields.currency; }; destinationAccountId: { from: instance; path: concat("fields.", guest_account_field); }; productId: { from: instance; path: productId; }; sourceAccountId: { from: instance; path: refs.bookingHoldingAccountId; }; }; capture: { cancellationTransferId: transferId; }; }];
|
|
79
|
+
steps: [];
|
|
80
|
+
}
|
|
81
|
+
action retain {
|
|
82
|
+
agent_description: concat("Pay the quoted cancellation penalty out of the holding account to the ", words(host), ". This moves money and does not reverse. The booking must already be canceled, so the penalty figure is the one the quote fixed.");
|
|
83
|
+
summary: concat("Pay the quoted penalty that the refund left behind to the ", words(host));
|
|
84
|
+
moves: [{ key: penalty; operation: internal_transfer.create; bind: { amount: { from: instance; path: refs.cancellationPenaltyAmount; }; currency: { from: instance; path: fields.currency; }; destinationAccountId: { from: instance; path: concat("fields.", host_account_field); }; productId: { from: instance; path: productId; }; sourceAccountId: { from: instance; path: refs.bookingHoldingAccountId; }; }; capture: { penaltyTransferId: transferId; }; }];
|
|
85
|
+
steps: [];
|
|
86
|
+
}
|
|
87
|
+
}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
module std.settlements.captured_payment
|
|
2
|
+
|
|
3
|
+
export instrument captured_payment<C>(payer: party, payee: party, amount: money<C>, reserve_until: date, correction: condition, external_reversal: condition, capture_mode: text, correction_mode: text, negative_position: text, timeout: text, derived_amount: optional<block>) {
|
|
4
|
+
let(correction_name): correction;
|
|
5
|
+
let(reversal_name): external_reversal;
|
|
6
|
+
let(reversal_window): external_reversal_within;
|
|
7
|
+
let(instrument_words): words(instrument);
|
|
8
|
+
when(derived_amount) {
|
|
9
|
+
let(derived_field): get(derived_amount, "field");
|
|
10
|
+
let(derived_source): get(derived_amount, "source");
|
|
11
|
+
let(derived_rule): get(derived_amount, "rule");
|
|
12
|
+
let(derived_bearer): get(derived_amount, "bearer");
|
|
13
|
+
}
|
|
14
|
+
agent_description: "Reach for captured payment when the {payee} reserves against the {payer} first and then takes the money in slices before {reserve_until}. Pick instant transfer when the whole amount moves at once, and deposit when the reservation is claimed or returned whole.";
|
|
15
|
+
summary: "Capture reservation from {payer} to {payee}";
|
|
16
|
+
title: sentence(instrument);
|
|
17
|
+
description: "Payer reservation captured by the payee in slices within a fixed window";
|
|
18
|
+
fields {
|
|
19
|
+
when(derived_amount) {
|
|
20
|
+
let(fee_collection_key): "x-hyperscale-fee-collection-port";
|
|
21
|
+
let(reference_filter_key): "x-hyperscale-reference-filter";
|
|
22
|
+
platformAccountId {
|
|
23
|
+
type: account<C>;
|
|
24
|
+
description: "The product fee collection account";
|
|
25
|
+
pattern: "^acct_(sandbox|live)_[a-z0-9]{8,64}$";
|
|
26
|
+
[fee_collection_key]: true;
|
|
27
|
+
[reference_filter_key]: { column: role; values: [customer_balance, product_revenue]; };
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
amount { type: money<C>; description: "Maximum captured amount in {C} minor units"; }
|
|
31
|
+
reserve_until { type: date; description: "Reservation expiry that releases any uncaptured remainder"; }
|
|
32
|
+
reversalUntil { type: date; description: "Machine-owned external reversal cutoff anchored when settlement completes"; optional: true; }
|
|
33
|
+
when(derived_amount) {
|
|
34
|
+
[derived_field] { type: money<C>; description: "Machine-computed {derived_rule} of {derived_source}; callers never supply it"; pattern: "^[1-9][0-9]{0,17}$"; optional: true; }
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
lifecycle {
|
|
38
|
+
states created authorized partially_captured settled voided expired corrected reversed;
|
|
39
|
+
initial created;
|
|
40
|
+
on authorize: created -> authorized;
|
|
41
|
+
on capture: authorized -> partially_captured;
|
|
42
|
+
on capture_more: partially_captured -> partially_captured;
|
|
43
|
+
on settle: authorized | partially_captured -> settled;
|
|
44
|
+
on void: authorized -> voided;
|
|
45
|
+
on expire: authorized -> expired;
|
|
46
|
+
on settle_on_expiry: partially_captured -> settled;
|
|
47
|
+
on [correction_name]: settled -> corrected;
|
|
48
|
+
on [reversal_name]: settled -> reversed;
|
|
49
|
+
}
|
|
50
|
+
parties {
|
|
51
|
+
payer: payer;
|
|
52
|
+
beneficiary: payee;
|
|
53
|
+
}
|
|
54
|
+
when(derived_amount) {
|
|
55
|
+
computes derived: { field: derived_field; source_field: derived_source; rounding: floor; rule: { kind: percentage_of; bps: derived_rule; }; };
|
|
56
|
+
}
|
|
57
|
+
action create {
|
|
58
|
+
let(metadata_instrument): "metadata.instrumentId";
|
|
59
|
+
let(metadata_instance): "metadata.instrumentInstanceId";
|
|
60
|
+
let(metadata_phase): "metadata.phase";
|
|
61
|
+
agent_description: "Open the payment row before anything is reserved. The caller supplies the {amount}, the currency, and both party accounts. This reserves nothing, call authorize next.";
|
|
62
|
+
summary: "Create a {instrument_words}";
|
|
63
|
+
when(derived_amount) {
|
|
64
|
+
moves: [{ key: "derived_amount"; operation: internal_transfer.create; bind: { amount: { from: instance; path: concat("fields.", derived_field); }; currency: { from: instance; path: fields.currency; }; destinationAccountId: { from: instance; path: fields.platformAccountId; }; [metadata_instrument]: { from: const; value: instrument; }; [metadata_instance]: { from: instance; path: instrumentInstanceId; }; [metadata_phase]: { from: const; value: create; }; productId: { from: instance; path: productId; }; sourceAccountId: { from: instance; path: concat("fields.", camel(derived_bearer), "AccountId"); }; }; capture: { createDerivedAmountTransferId: transferId; }; }];
|
|
65
|
+
}
|
|
66
|
+
steps: [];
|
|
67
|
+
}
|
|
68
|
+
action authorize {
|
|
69
|
+
agent_description: "Reserve the {amount} against the {payer} in the {payee}'s favor until {reserve_until}. The payment must be created. The money is held, not paid: capture and settle post it, void and expiry release it.";
|
|
70
|
+
summary: "Reserve the {amount} until {reserve_until}";
|
|
71
|
+
moves: [{ key: reservation; amount: amount; from: payer; to: beneficiary; operation: reserve; }];
|
|
72
|
+
steps: [];
|
|
73
|
+
}
|
|
74
|
+
action capture {
|
|
75
|
+
agent_description: "Post the first capture slice out of the live reservation. The payment must be authorized and the caller passes captureAmount in minor units. The slice settles for good and void can no longer release the reservation. Call before {reserve_until}.";
|
|
76
|
+
summary: "Post one strict partial capture slice";
|
|
77
|
+
deadline { field: reserve_until; };
|
|
78
|
+
input { type: object; additional_properties: false; properties: { captureAmount: { description: "Positive partial capture amount in minor units"; pattern: "^[1-9][0-9]{0,17}$"; type: string; }; }; required: [captureAmount]; }
|
|
79
|
+
moves: [{ key: post; operation: internal_transfer.post; bind: { amount: { from: input; path: captureAmount; }; currency: { from: instance; path: fields.currency; }; postMode: { from: const; value: partial_only; }; transferId: { from: instance; path: refs.authorizeReservationTransferId; }; }; capture: { capturedAmount: postedAmount; }; }];
|
|
80
|
+
steps: [];
|
|
81
|
+
}
|
|
82
|
+
action capture_more {
|
|
83
|
+
agent_description: "Post another capture slice out of a partly captured reservation. Call this only after capture took the first slice. Each slice settles for good, and the uncaptured remainder still releases at {reserve_until}.";
|
|
84
|
+
summary: "Post another strict partial capture slice";
|
|
85
|
+
deadline { field: reserve_until; };
|
|
86
|
+
input { type: object; additional_properties: false; properties: { captureAmount: { description: "Positive partial capture amount in minor units"; pattern: "^[1-9][0-9]{0,17}$"; type: string; }; }; required: [captureAmount]; }
|
|
87
|
+
moves: [{ key: post; operation: internal_transfer.post; bind: { amount: { from: input; path: captureAmount; }; currency: { from: instance; path: fields.currency; }; postMode: { from: const; value: partial_only; }; transferId: { from: instance; path: refs.authorizeReservationTransferId; }; }; capture: { capturedAmount: postedAmount; }; }];
|
|
88
|
+
steps: [];
|
|
89
|
+
}
|
|
90
|
+
action settle {
|
|
91
|
+
agent_description: "Post the whole reserved remainder and finish the payment. The payment must be authorized or partly captured. The money settles and the external reversal window opens on reversalUntil.";
|
|
92
|
+
summary: "Post the full reserved remainder and settle";
|
|
93
|
+
deadline { field: reserve_until; };
|
|
94
|
+
sets(at) { field: reversalUntil; offset: reversal_window; };
|
|
95
|
+
moves: [{ key: post; operation: internal_transfer.post; bind: { transferId: { from: instance; path: refs.authorizeReservationTransferId; }; }; capture: { capturedAmount: postedAmount; }; }];
|
|
96
|
+
steps: [];
|
|
97
|
+
}
|
|
98
|
+
action void {
|
|
99
|
+
agent_description: "Release the reservation whole and cancel the payment. Only an authorized reservation with nothing captured can void. Once any slice is captured, settle it or let it expire instead.";
|
|
100
|
+
summary: "Release an entirely uncaptured reservation";
|
|
101
|
+
moves: [{ key: void; operation: internal_transfer.void; bind: { reason: { from: const; value: "Reservation voided before any capture"; }; transferId: { from: instance; path: refs.authorizeReservationTransferId; }; }; }];
|
|
102
|
+
steps: [];
|
|
103
|
+
}
|
|
104
|
+
action expire {
|
|
105
|
+
summary: "Release an uncaptured reservation at expiry";
|
|
106
|
+
due { field: reserve_until; };
|
|
107
|
+
moves: [{ key: void; operation: internal_transfer.void; bind: { reason: { from: const; value: "Uncaptured reservation expired"; }; transferId: { from: instance; path: refs.authorizeReservationTransferId; }; }; }];
|
|
108
|
+
steps: [];
|
|
109
|
+
}
|
|
110
|
+
action settle_on_expiry {
|
|
111
|
+
summary: "Release the uncaptured remainder and settle captured slices";
|
|
112
|
+
due { field: reserve_until; };
|
|
113
|
+
sets(at) { field: reversalUntil; offset: reversal_window; };
|
|
114
|
+
moves: [{ key: void; operation: internal_transfer.void; bind: { reason: { from: const; value: "Uncaptured remainder released at expiry"; }; transferId: { from: instance; path: refs.authorizeReservationTransferId; }; }; }];
|
|
115
|
+
steps: [];
|
|
116
|
+
}
|
|
117
|
+
action [correction_name] {
|
|
118
|
+
let(metadata_clawback): "metadata.clawbackOfTransferId";
|
|
119
|
+
let(metadata_instrument): "metadata.instrumentId";
|
|
120
|
+
let(metadata_instance): "metadata.instrumentInstanceId";
|
|
121
|
+
let(metadata_phase): "metadata.phase";
|
|
122
|
+
let(correction_capture): camel(concat(correction_name, "_transfer_transfer_id"));
|
|
123
|
+
agent_description: "Return the whole captured amount to the {payer} after the payment settled. Only a settled payment corrects, and only the party the product authorized may call it. The money moves back and there is no undo.";
|
|
124
|
+
summary: "Return the full captured amount on payee correction";
|
|
125
|
+
port { allowed_parties: [if_eq(at(correction_allowed, 1), payee, "beneficiary", "payer")]; };
|
|
126
|
+
moves: [{ key: transfer; operation: internal_transfer.create; bind: { amount: { from: instance; path: refs.capturedAmount; }; currency: { from: instance; path: fields.currency; }; destinationAccountId: { from: instance; path: concat("fields.", payer, "AccountId"); }; [metadata_clawback]: { from: instance; path: refs.authorizeReservationTransferId; }; [metadata_instrument]: { from: const; value: instrument; }; [metadata_instance]: { from: instance; path: instrumentInstanceId; }; [metadata_phase]: { from: const; value: correction_name; }; productId: { from: instance; path: productId; }; sourceAccountId: { from: instance; path: concat("fields.", payee, "AccountId"); }; }; capture: { [correction_capture]: transferId; }; }];
|
|
127
|
+
steps: [];
|
|
128
|
+
}
|
|
129
|
+
action [reversal_name] {
|
|
130
|
+
let(metadata_clawback): "metadata.clawbackOfTransferId";
|
|
131
|
+
let(metadata_instrument): "metadata.instrumentId";
|
|
132
|
+
let(metadata_instance): "metadata.instrumentInstanceId";
|
|
133
|
+
let(metadata_phase): "metadata.phase";
|
|
134
|
+
let(reversal_capture): camel(concat(reversal_name, "_transfer_transfer_id"));
|
|
135
|
+
agent_description: "Return the whole captured amount to the {payer} on an external reversal. The payment must be settled and the call must land before reversalUntil. The caller passes externalReference, which stays in the receipt. There is no undo.";
|
|
136
|
+
summary: "Return the full captured amount on an external reversal";
|
|
137
|
+
capture(input) { externalReference: externalReference; };
|
|
138
|
+
deadline { field: reversalUntil; };
|
|
139
|
+
input { type: object; additional_properties: false; properties: { externalReference: { description: "Required decision reference retained in the operation receipt"; max_length: 180; min_length: 1; type: string; }; }; required: [externalReference]; }
|
|
140
|
+
port { allowed_parties: [if_eq(at(external_reversal_allowed, 1), payee, "beneficiary", "payer")]; };
|
|
141
|
+
moves: [{ key: transfer; operation: internal_transfer.create; bind: { amount: { from: instance; path: refs.capturedAmount; }; currency: { from: instance; path: fields.currency; }; destinationAccountId: { from: instance; path: concat("fields.", payer, "AccountId"); }; [metadata_clawback]: { from: instance; path: refs.authorizeReservationTransferId; }; [metadata_instrument]: { from: const; value: instrument; }; [metadata_instance]: { from: instance; path: instrumentInstanceId; }; [metadata_phase]: { from: const; value: reversal_name; }; productId: { from: instance; path: productId; }; sourceAccountId: { from: instance; path: concat("fields.", payee, "AccountId"); }; }; capture: { [reversal_capture]: transferId; }; }];
|
|
142
|
+
steps: [];
|
|
143
|
+
}
|
|
144
|
+
}
|