@hyperscale0/hsx 2.4.1 → 3.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 +2 -408
- package/README.md +3 -92
- package/dist/src/ast.d.ts +99 -310
- package/dist/src/ast.d.ts.map +1 -1
- package/dist/src/ast.js +16 -69
- package/dist/src/ast.js.map +1 -1
- package/dist/src/cli.d.ts +6 -19
- package/dist/src/cli.d.ts.map +1 -1
- package/dist/src/cli.js +76 -315
- package/dist/src/cli.js.map +1 -1
- package/dist/src/compile.d.ts +23 -73
- package/dist/src/compile.d.ts.map +1 -1
- package/dist/src/compile.js +1113 -148
- package/dist/src/compile.js.map +1 -1
- package/dist/src/cost.d.ts +8 -77
- package/dist/src/cost.d.ts.map +1 -1
- package/dist/src/cost.js +13 -422
- package/dist/src/cost.js.map +1 -1
- package/dist/src/format.d.ts +8 -7
- package/dist/src/format.d.ts.map +1 -1
- package/dist/src/format.js +9 -175
- package/dist/src/format.js.map +1 -1
- package/dist/src/headers.d.ts +35 -0
- package/dist/src/headers.d.ts.map +1 -0
- package/dist/src/headers.js +108 -0
- package/dist/src/headers.js.map +1 -0
- package/dist/src/index.d.ts +6 -12
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +4 -9
- package/dist/src/index.js.map +1 -1
- package/dist/src/lex.d.ts +8 -28
- package/dist/src/lex.d.ts.map +1 -1
- package/dist/src/lex.js +88 -146
- package/dist/src/lex.js.map +1 -1
- package/dist/src/parse.d.ts +4 -17
- package/dist/src/parse.d.ts.map +1 -1
- package/dist/src/parse.js +402 -1040
- package/dist/src/parse.js.map +1 -1
- package/dist/src/std-bundle.d.ts.map +1 -1
- package/dist/src/std-bundle.js +12 -20
- package/dist/src/std-bundle.js.map +1 -1
- package/dist/src/std-library.d.ts +1 -1
- package/dist/src/std-library.d.ts.map +1 -1
- package/dist/src/std-library.js +1 -5
- package/dist/src/std-library.js.map +1 -1
- package/dist/src/tunables.d.ts +7 -0
- package/dist/src/tunables.d.ts.map +1 -0
- package/dist/src/tunables.js +26 -0
- package/dist/src/tunables.js.map +1 -0
- package/dist/src/version.d.ts +2 -4
- package/dist/src/version.d.ts.map +1 -1
- package/dist/src/version.js +2 -4
- package/dist/src/version.js.map +1 -1
- package/docs/README.md +179 -41
- package/docs/headers.md +44 -0
- package/examples/cost-table.json +99 -751
- package/examples/library.hsx +59 -0
- package/package.json +9 -17
- package/src/ast.ts +82 -441
- package/src/cli.ts +77 -362
- package/src/compile.ts +1535 -247
- package/src/cost.ts +28 -671
- package/src/format.ts +12 -256
- package/src/headers.ts +126 -0
- package/src/index.ts +7 -39
- package/src/lex.ts +99 -195
- package/src/parse.ts +403 -1209
- package/src/std-bundle.ts +12 -20
- package/src/std-library.ts +2 -7
- package/src/tunables.ts +31 -0
- package/src/version.ts +2 -5
- package/std/approvals.hsx +17 -0
- package/std/cards.hsx +63 -0
- package/std/collections.hsx +31 -0
- package/std/escrow.hsx +43 -0
- package/std/financing.hsx +372 -0
- package/std/insurance.hsx +59 -0
- package/std/lending.hsx +115 -0
- package/std/marketplace.hsx +24 -0
- package/std/money.hsx +195 -0
- package/std/savings.hsx +44 -0
- package/std/travel.hsx +98 -0
- package/std/wallet.hsx +41 -0
- package/dist/src/diagnostics.d.ts +0 -13
- package/dist/src/diagnostics.d.ts.map +0 -1
- package/dist/src/diagnostics.js +0 -587
- package/dist/src/diagnostics.js.map +0 -1
- package/dist/src/emit.d.ts +0 -51
- package/dist/src/emit.d.ts.map +0 -1
- package/dist/src/emit.js +0 -192
- package/dist/src/emit.js.map +0 -1
- package/dist/src/entry-overrides.d.ts +0 -58
- package/dist/src/entry-overrides.d.ts.map +0 -1
- package/dist/src/entry-overrides.js +0 -284
- package/dist/src/entry-overrides.js.map +0 -1
- package/dist/src/ir.d.ts +0 -73
- package/dist/src/ir.d.ts.map +0 -1
- package/dist/src/ir.js +0 -15
- package/dist/src/ir.js.map +0 -1
- package/dist/src/limits.d.ts +0 -23
- package/dist/src/limits.d.ts.map +0 -1
- package/dist/src/limits.js +0 -23
- package/dist/src/limits.js.map +0 -1
- package/dist/src/lsp/server.d.ts +0 -32
- package/dist/src/lsp/server.d.ts.map +0 -1
- package/dist/src/lsp/server.js +0 -391
- package/dist/src/lsp/server.js.map +0 -1
- package/dist/src/modules.d.ts +0 -38
- package/dist/src/modules.d.ts.map +0 -1
- package/dist/src/modules.js +0 -372
- package/dist/src/modules.js.map +0 -1
- package/dist/src/typecheck.d.ts +0 -8
- package/dist/src/typecheck.d.ts.map +0 -1
- package/dist/src/typecheck.js +0 -4160
- package/dist/src/typecheck.js.map +0 -1
- package/docs/guide/01-first-program.md +0 -24
- package/docs/guide/02-money.md +0 -25
- package/docs/guide/03-instruments.md +0 -93
- package/docs/guide/04-lifecycles.md +0 -40
- package/docs/guide/05-fees-and-splits.md +0 -45
- package/docs/guide/06-schedules.md +0 -26
- package/docs/guide/07-composition.md +0 -52
- package/docs/guide/08-writing-a-module.md +0 -85
- package/docs/guide/09-cost.md +0 -24
- package/docs/guide/10-diagnostics.md +0 -27
- package/docs/llms-full.txt +0 -3684
- package/docs/llms.txt +0 -44
- package/docs/piece-plans.md +0 -120
- package/docs/playground.md +0 -55
- package/docs/reference/cli.md +0 -36
- package/docs/reference/diagnostics.md +0 -901
- package/docs/reference/grammar.md +0 -38
- package/docs/reference/std/advance.md +0 -112
- package/docs/reference/std/cancellable_booking.md +0 -159
- package/docs/reference/std/captured_payment.md +0 -115
- package/docs/reference/std/conditional_disbursement.md +0 -101
- package/docs/reference/std/credit_facility.md +0 -108
- package/docs/reference/std/held_payment.md +0 -173
- package/docs/reference/std/instant_transfer.md +0 -97
- package/docs/reference/std/metered.md +0 -89
- package/docs/reference/std/pooled_split.md +0 -95
- package/docs/reference/std/premium_forward.md +0 -119
- package/docs/reference/std/reconciled_payout.md +0 -103
- package/docs/reference/std/rotating_pool.md +0 -130
- package/docs/reference/std/scheduled.md +0 -136
- package/docs/reference/std/security_deposit.md +0 -138
- package/docs/reference/std/settlement_batch.md +0 -123
- package/docs/reference/std/swap.md +0 -133
- package/docs/reference/std/threshold_pool.md +0 -138
- package/docs/reference/std/weighted_distribution.md +0 -135
- package/docs/reference/types.md +0 -61
- package/docs/reference/udl-output.md +0 -13
- package/examples/01-first-program/README.md +0 -45
- package/examples/01-first-program/tip-jar.hsx +0 -17
- package/examples/02-imports-and-modules/README.md +0 -96
- package/examples/02-imports-and-modules/photo-booth.hsx +0 -37
- package/examples/03-diagnostics/README.md +0 -68
- package/examples/03-diagnostics/corner-shop-fixed.hsx +0 -22
- package/examples/03-diagnostics/corner-shop.hsx +0 -19
- package/examples/04-complete-product/README.md +0 -84
- package/examples/04-complete-product/study-hall.hsx +0 -67
- package/examples/05-authored-instrument/README.md +0 -5
- package/examples/05-authored-instrument/payment.hsx +0 -37
- package/examples/05-watch-club/README.md +0 -6
- package/examples/05-watch-club/watch-club.hsx +0 -123
- package/examples/README.md +0 -48
- package/examples/advance/README.md +0 -3
- package/examples/advance/advance.hsx +0 -13
- package/examples/advance/advance.udl +0 -367
- package/examples/cancellable_booking/README.md +0 -3
- package/examples/cancellable_booking/cancellable_booking.hsx +0 -14
- package/examples/cancellable_booking/cancellable_booking.udl +0 -413
- package/examples/captured_payment/README.md +0 -3
- package/examples/captured_payment/captured_payment.hsx +0 -17
- package/examples/captured_payment/captured_payment.udl +0 -686
- package/examples/conditional_disbursement/README.md +0 -3
- package/examples/conditional_disbursement/conditional_disbursement.hsx +0 -15
- package/examples/conditional_disbursement/conditional_disbursement.udl +0 -418
- package/examples/credit_facility/README.md +0 -3
- package/examples/credit_facility/credit_facility.hsx +0 -24
- package/examples/credit_facility/credit_facility.udl +0 -1396
- package/examples/held_payment/README.md +0 -3
- package/examples/held_payment/held_payment.hsx +0 -13
- package/examples/held_payment/held_payment.udl +0 -569
- package/examples/instant_transfer/README.md +0 -3
- package/examples/instant_transfer/instant_transfer.hsx +0 -16
- package/examples/instant_transfer/instant_transfer.udl +0 -376
- package/examples/metered/README.md +0 -3
- package/examples/metered/metered.hsx +0 -13
- package/examples/metered/metered.udl +0 -253
- package/examples/pooled_split/README.md +0 -3
- package/examples/pooled_split/pooled_split.hsx +0 -15
- package/examples/pooled_split/pooled_split.udl +0 -432
- package/examples/premium_forward/README.md +0 -3
- package/examples/premium_forward/premium_forward.hsx +0 -19
- package/examples/premium_forward/premium_forward.udl +0 -604
- package/examples/reconciled_payout/README.md +0 -3
- package/examples/reconciled_payout/reconciled_payout.hsx +0 -13
- package/examples/reconciled_payout/reconciled_payout.udl +0 -312
- package/examples/rotating_pool/README.md +0 -3
- package/examples/rotating_pool/rotating_pool.hsx +0 -18
- package/examples/rotating_pool/rotating_pool.udl +0 -4370
- package/examples/scheduled/README.md +0 -3
- package/examples/scheduled/scheduled.hsx +0 -12
- package/examples/scheduled/scheduled.udl +0 -355
- package/examples/security_deposit/README.md +0 -3
- package/examples/security_deposit/security_deposit.hsx +0 -21
- package/examples/security_deposit/security_deposit.udl +0 -320
- package/examples/settlement_batch/README.md +0 -3
- package/examples/settlement_batch/settlement_batch.hsx +0 -19
- package/examples/settlement_batch/settlement_batch.udl +0 -757
- package/examples/swap/README.md +0 -3
- package/examples/swap/swap.hsx +0 -19
- package/examples/swap/swap.udl +0 -892
- package/examples/threshold_pool/README.md +0 -3
- package/examples/threshold_pool/threshold_pool.hsx +0 -16
- package/examples/threshold_pool/threshold_pool.udl +0 -680
- package/examples/weighted_distribution/README.md +0 -3
- package/examples/weighted_distribution/weighted_distribution.hsx +0 -20
- package/examples/weighted_distribution/weighted_distribution.udl +0 -348
- package/skills/hsx/SKILL.md +0 -449
- package/src/diagnostics.ts +0 -620
- package/src/emit.ts +0 -282
- package/src/entry-overrides.ts +0 -428
- package/src/ir.ts +0 -113
- package/src/limits.ts +0 -22
- package/src/lsp/server.ts +0 -460
- package/src/modules.ts +0 -452
- package/src/typecheck.ts +0 -5515
- package/std/SEMANTICS.md +0 -44
- package/std/money_flows/advance.hsx +0 -235
- package/std/money_flows/cancellable_booking.hsx +0 -402
- package/std/money_flows/captured_payment.hsx +0 -220
- package/std/money_flows/conditional_disbursement.hsx +0 -226
- package/std/money_flows/credit_facility.hsx +0 -214
- package/std/money_flows/held_payment.hsx +0 -812
- package/std/money_flows/index.hsx +0 -4
- package/std/money_flows/instant_transfer.hsx +0 -231
- package/std/money_flows/metered.hsx +0 -104
- package/std/money_flows/pooled_split.hsx +0 -109
- package/std/money_flows/premium_forward.hsx +0 -307
- package/std/money_flows/reconciled_payout.hsx +0 -119
- package/std/money_flows/rotating_pool.hsx +0 -305
- package/std/money_flows/scheduled.hsx +0 -599
- package/std/money_flows/security_deposit.hsx +0 -248
- package/std/money_flows/settlement_batch.hsx +0 -173
- package/std/money_flows/swap.hsx +0 -1192
- package/std/money_flows/threshold_pool.hsx +0 -307
- package/std/money_flows/weighted_distribution.hsx +0 -237
|
@@ -1,812 +0,0 @@
|
|
|
1
|
-
module std.money_flows.held_payment
|
|
2
|
-
|
|
3
|
-
// Escrow settlement holding funds from a payer before releasing to a payee upon a verified condition or deadline.
|
|
4
|
-
//
|
|
5
|
-
// ### Purpose
|
|
6
|
-
// `held_payment` holds customer funds in a dedicated product escrow account away from both payer and payee.
|
|
7
|
-
// It fits milestone-gated commerce, vehicle escrow, contractor holdbacks, and goods purchases where funds
|
|
8
|
-
// must remain reserved until delivery confirmation or inspection.
|
|
9
|
-
//
|
|
10
|
-
// ### Selection guidance
|
|
11
|
-
// - vs `cancellable_booking`: Both hold funds in custody and both can quote a cancellation before it is
|
|
12
|
-
// spent. `held_payment` quotes one flat charge declared by `cancel_charge_bps`, because it has no scheduled
|
|
13
|
-
// start to price against, and `on_cancel` remains the way to unwind it on static splits instead.
|
|
14
|
-
// Choose `cancellable_booking` when the charge must follow the time left before a scheduled start date.
|
|
15
|
-
// - vs `security_deposit`: `held_payment` releases or cancels the principal according to predefined splits.
|
|
16
|
-
// Choose `security_deposit` when the holder must assess damages and claim an arbitrary partial amount
|
|
17
|
-
// via a `decided amount` clause while returning the unspent remainder to the payer.
|
|
18
|
-
// - vs `swap`: `held_payment` is a one-way transfer from payer to payee. Choose `swap` for bilateral or
|
|
19
|
-
// multi-party atomic exchanges where all parties must fund their legs into escrow before simultaneous release.
|
|
20
|
-
// - vs `premium_forward`: Choose `premium_forward` for insurance premium collection requiring carrier policy
|
|
21
|
-
// binding conditions, broker commission retention, policy endorsements, and lapse schedules.
|
|
22
|
-
//
|
|
23
|
-
// ### Parameters
|
|
24
|
-
// - `payer`: The funding party providing the money.
|
|
25
|
-
// - `payee`: The beneficiary party receiving the released funds.
|
|
26
|
-
// - `amount`: Total amount in minor units of currency `C`.
|
|
27
|
-
// - `release`: Condition required to release funds. Supports decision ports (`port <name>`), date deadlines
|
|
28
|
-
// (`at(<date>)`), or disjunctions (`port <name> | at(<date>)`).
|
|
29
|
-
// - `fees`: Optional block declaring percentage or fixed fee cuts, e.g. `{ buyer: 1%, seller: 2% }`.
|
|
30
|
-
// - `on_cancel`: Optional block defining refund splits if cancelled while funded, e.g. `(funded) { buyer: 90%, seller: 10% }`.
|
|
31
|
-
// - `derived_amount`: Optional block declaring machine-derived fees calculated as a percentage of another field.
|
|
32
|
-
// - `release_to`: Optional third-party destination for release.
|
|
33
|
-
// - `whole_amount`: Optional block enabling single-action funding and release of principal plus on-top fee.
|
|
34
|
-
// - `release_action`: Optional custom name for the release action.
|
|
35
|
-
// - `whole_fee`: Optional money amount for the whole-amount fee.
|
|
36
|
-
// - `reference`: Optional string reference stored on the instance.
|
|
37
|
-
// - `upstream`: Optional reference to a parent instrument.
|
|
38
|
-
// - `id_prefix_override`: Optional custom prefix for generated instrument IDs.
|
|
39
|
-
// - `cancel_charge_bps`: Optional cancellation charge in basis points. Declaring it gives the settlement a
|
|
40
|
-
// quoted cancellation: `quote_cancellation` prices the charge and the refund and freezes both,
|
|
41
|
-
// `cancel` pays the refund to the payer, and `retain_cancellation_charge` pays the charge to the payee.
|
|
42
|
-
// A zero charge keeps the flow with a zero fee.
|
|
43
|
-
// - `cancel_offer_life`: ISO 8601 duration a cancellation quote stays open, required with `cancel_charge_bps`.
|
|
44
|
-
//
|
|
45
|
-
// - `private_actions`: Suppress automatic aliases. Publish chosen actions with `expose`.
|
|
46
|
-
//
|
|
47
|
-
// ### Decision ports
|
|
48
|
-
// - `release`: Port deciding release authorization, answered by allowed parties declared in the port.
|
|
49
|
-
//
|
|
50
|
-
// ### Example
|
|
51
|
-
// ```hsx
|
|
52
|
-
// program held_payment_example "Held payment example"
|
|
53
|
-
// import { held_payment } from "std/money_flows"
|
|
54
|
-
// party buyer: person
|
|
55
|
-
// party seller: business
|
|
56
|
-
// settlement sale = held_payment {
|
|
57
|
-
// payer: buyer
|
|
58
|
-
// payee: seller
|
|
59
|
-
// amount: price: money(SAR)
|
|
60
|
-
// fees { buyer: 1% }
|
|
61
|
-
// on_cancel(funded) { buyer: 100% }
|
|
62
|
-
// release: port confirm_delivery | at(releaseDueAt)
|
|
63
|
-
// }
|
|
64
|
-
// port confirm_delivery { allowed: [buyer] }
|
|
65
|
-
// ```
|
|
66
|
-
export instrument held_payment<C>(payer: party, payee: party, amount: money<C>, release: condition, fees: optional<block>, on_cancel: optional<block>, derived_amount: optional<block>, release_to: optional<party>, whole_amount: optional<block>, release_action: optional<text>, whole_fee: optional<money<C>>, reference: optional<text>, upstream: optional<ref>, id_prefix_override: optional<text>, cancel_charge_bps: optional<integer>, cancel_offer_life: optional<text>, private_actions: optional<boolean>) {
|
|
67
|
-
let(release_name): release;
|
|
68
|
-
let(release_actor_text): if_eq(len(release_allowed), 1, words(at(release_allowed, 1)), concat("a party allowed by ", release));
|
|
69
|
-
let(payer_account_field): camel(concat(payer, "_account_id"));
|
|
70
|
-
let(payee_account_field): camel(concat(payee, "_account_id"));
|
|
71
|
-
let(release_to_account_field): camel(concat(release_to, "_account_id"));
|
|
72
|
-
let(payer_fee): get(fees, payer);
|
|
73
|
-
let(payee_fee): get(fees, payee);
|
|
74
|
-
let(payer_fee_kind): kind(payer_fee);
|
|
75
|
-
let(payee_fee_kind): kind(payee_fee);
|
|
76
|
-
let(has_cancel_quote): if_eq(kind(cancel_charge_bps), "boolean", false, true);
|
|
77
|
-
let(has_charge): if_eq(cancel_charge_bps, 0, false, true);
|
|
78
|
-
when_eq(payee_fee_kind, binding) {
|
|
79
|
-
when(on_cancel) {
|
|
80
|
-
unsupported {
|
|
81
|
-
code: HSX1110;
|
|
82
|
-
message: "exact payee fees cannot combine with on_cancel yet";
|
|
83
|
-
fix: "remove on_cancel or use a percentage payee fee";
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
when(has_cancel_quote) {
|
|
88
|
-
when(on_cancel) {
|
|
89
|
-
unsupported {
|
|
90
|
-
code: HSX1110;
|
|
91
|
-
message: "a quoted cancellation cannot combine with on_cancel splits";
|
|
92
|
-
fix: "drop on_cancel and let cancel_charge_bps price the cancellation";
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
when(payee_fee) {
|
|
96
|
-
unsupported {
|
|
97
|
-
code: HSX1110;
|
|
98
|
-
message: "a quoted cancellation cannot combine with a payee fee";
|
|
99
|
-
fix: "drop the payee fee, or unwind through on_cancel splits instead of a quote";
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
when(whole_amount) {
|
|
103
|
-
unsupported {
|
|
104
|
-
code: HSX1110;
|
|
105
|
-
message: "a quoted cancellation cannot combine with whole_amount";
|
|
106
|
-
fix: "drop whole_amount, which cancels the whole hold without quoting it";
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
when_not(cancel_offer_life) {
|
|
110
|
-
unsupported {
|
|
111
|
-
code: HSX1110;
|
|
112
|
-
message: "a quoted cancellation needs cancel_offer_life";
|
|
113
|
-
fix: "declare how long the quote stays open, for example PT30M";
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
when(cancel_offer_life) {
|
|
118
|
-
when_not(has_cancel_quote) {
|
|
119
|
-
unsupported {
|
|
120
|
-
code: HSX1110;
|
|
121
|
-
message: "cancel_offer_life needs cancel_charge_bps to quote";
|
|
122
|
-
fix: "declare the cancellation charge in basis points, or drop cancel_offer_life";
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
let(payer_bps): basis_points(payer_fee);
|
|
127
|
-
let(payee_bps): basis_points(payee_fee);
|
|
128
|
-
let(net_bps): sub(10000, payee_fee);
|
|
129
|
-
let(net_percent): percent_text(net_bps);
|
|
130
|
-
let(cancel_payer): get(on_cancel, payer);
|
|
131
|
-
let(cancel_payee): get(on_cancel, payee);
|
|
132
|
-
let(cancel_payer_bps): basis_points(cancel_payer);
|
|
133
|
-
let(cancel_payee_bps): basis_points(cancel_payee);
|
|
134
|
-
let(cancel_splits): lt(1, len(on_cancel));
|
|
135
|
-
let(piece1_bps): min(net_bps, cancel_payer_bps);
|
|
136
|
-
let(piece3_bps): min(payee_bps, cancel_payee_bps);
|
|
137
|
-
let(piece2_bps): sub(sub(10000, piece1_bps), piece3_bps);
|
|
138
|
-
let(piece1_percent): percent_text(piece1_bps);
|
|
139
|
-
let(piece2_percent): percent_text(piece2_bps);
|
|
140
|
-
let(piece3_percent): percent_text(piece3_bps);
|
|
141
|
-
let(carved_spans): lt(cancel_payee_bps, payee_bps);
|
|
142
|
-
let(derived_field): get(derived_amount, "field");
|
|
143
|
-
let(derived_source): get(derived_amount, "source");
|
|
144
|
-
let(derived_rule): get(derived_amount, "rule");
|
|
145
|
-
let(derived_bearer): get(derived_amount, "bearer");
|
|
146
|
-
agent_description: concat("Reach for ", words(instrument), " when the ", words(payer), " must commit money up front and nobody is paid until ", release, " fires. Funding moves ", amount, " into this settlement's own escrow account, where it stays until a release or an unwind pays it out. A straight-through payment with no custody step is a different shape.");
|
|
147
|
-
when_not(whole_amount) {
|
|
148
|
-
summary: concat("Escrow-held payment from ", words(payer), " to ", words(payee));
|
|
149
|
-
title: sentence(instrument);
|
|
150
|
-
when_not(release_to) { description: concat("Held payment: the ", words(payer), " funds ", amount, " into this settlement's own escrow; ", release_actor_text, " confirms through ", release, " to release"); }
|
|
151
|
-
when(release_to) { description: concat("Held payment: the ", words(payer), " funds ", amount, " into this settlement's own escrow; ", release_actor_text, " confirms through ", release, " to release to the ", words(release_to), ", whose advance the ", words(payee), " repays out of it"); }
|
|
152
|
-
fields {
|
|
153
|
-
let(fee_collection_key): "x-hyperscale-fee-collection-port";
|
|
154
|
-
let(reference_filter_key): "x-hyperscale-reference-filter";
|
|
155
|
-
when(release_to) {
|
|
156
|
-
[release_to_account_field] { type: account<C>; description: concat("The ", words(release_to), " account"); [reference_filter_key]: { column: role; values: [customer_balance]; }; }
|
|
157
|
-
}
|
|
158
|
-
platformAccountId {
|
|
159
|
-
type: account<C>;
|
|
160
|
-
description: "The product fee collection account";
|
|
161
|
-
[fee_collection_key]: true;
|
|
162
|
-
[reference_filter_key]: { column: role; values: [customer_balance, product_revenue]; };
|
|
163
|
-
}
|
|
164
|
-
when_not(payee_fee) {
|
|
165
|
-
when_not(cancel_splits) { amount { type: money<C>; description: "The held amount in {C} minor units, funded and paid out whole"; } }
|
|
166
|
-
when(cancel_splits) {
|
|
167
|
-
amount { type: money<C>; description: "The gross held amount in {C} minor units; the piece fields below partition it exactly"; }
|
|
168
|
-
piece1Amount { type: money<C>; description: concat(cancel_payer, " of ", amount, " (carries the integer-division remainder): released to the ", payee, "; on cancellation to the ", payer, ". Computed as floor(", amount, " * ", cancel_payer_bps, " / 10000) in ", C, " minor units"); pattern: "^[1-9][0-9]{0,17}$"; }
|
|
169
|
-
piece2Amount { type: money<C>; description: concat(cancel_payee, " of ", amount, ": released to the ", payee, "; on cancellation to the ", payee, ". Computed as floor(", amount, " * ", cancel_payee_bps, " / 10000) in ", C, " minor units"); pattern: "^[1-9][0-9]{0,17}$"; }
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
when(payee_fee) {
|
|
173
|
-
amount { type: money<C>; description: "The gross held amount in {C} minor units; the piece fields below partition it exactly"; }
|
|
174
|
-
when_not(on_cancel) {
|
|
175
|
-
when_eq(payee_fee_kind, percent) {
|
|
176
|
-
piece1Amount { type: money<C>; description: concat(net_percent, " of ", amount, " (carries the integer-division remainder): released to the ", payee, ". Computed as floor(", amount, " * ", net_bps, " / 10000) in ", C, " minor units"); pattern: "^[1-9][0-9]{0,17}$"; }
|
|
177
|
-
piece2Amount { type: money<C>; description: concat(payee_fee, " of ", amount, ": released to the platform. Computed as floor(", amount, " * ", payee_bps, " / 10000) in ", C, " minor units"); pattern: "^[1-9][0-9]{0,17}$"; optional: true; }
|
|
178
|
-
}
|
|
179
|
-
when_eq(payee_fee_kind, binding) {
|
|
180
|
-
piece1Amount { type: money<C>; description: concat("The remainder of ", amount, " after the exact ", payee_fee, " fee; released to the ", payee); pattern: "^[1-9][0-9]{0,17}$"; }
|
|
181
|
-
[payee_fee] { type: payee_fee; description: concat("Immutable exact ", payee, " fee in ", C, " minor units; released to the platform"); pattern: "^[1-9][0-9]{0,17}$"; }
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
when(on_cancel) {
|
|
185
|
-
piece1Amount { type: money<C>; description: concat(piece1_percent, " of ", amount, " (carries the integer-division remainder): released to the ", payee, "; on cancellation to the ", payer, ". Computed as floor(", amount, " * ", piece1_bps, " / 10000) in ", C, " minor units"); pattern: "^[1-9][0-9]{0,17}$"; }
|
|
186
|
-
when(carved_spans) {
|
|
187
|
-
piece2Amount { type: money<C>; description: concat(piece2_percent, " of ", amount, ": released to the platform; on cancellation to the ", payer, ". Computed as floor(", amount, " * ", piece2_bps, " / 10000) in ", C, " minor units"); pattern: "^[1-9][0-9]{0,17}$"; }
|
|
188
|
-
piece3Amount { type: money<C>; description: concat(piece3_percent, " of ", amount, ": released to the platform; on cancellation to the ", payee, ". Computed as floor(", amount, " * ", piece3_bps, " / 10000) in ", C, " minor units"); pattern: "^[1-9][0-9]{0,17}$"; }
|
|
189
|
-
carvedFeeAmount { type: money<C>; description: "Machine-owned total of the 2 carved platform fee pieces"; pattern: "^[1-9][0-9]{0,17}$"; optional: true; }
|
|
190
|
-
}
|
|
191
|
-
when_not(carved_spans) {
|
|
192
|
-
piece2Amount { type: money<C>; description: concat(piece2_percent, " of ", amount, ": released to the ", payee, "; on cancellation to the ", payee, ". Computed as floor(", amount, " * ", piece2_bps, " / 10000) in ", C, " minor units"); pattern: "^[1-9][0-9]{0,17}$"; }
|
|
193
|
-
piece3Amount { type: money<C>; description: concat(piece3_percent, " of ", amount, ": released to the platform; on cancellation to the ", payee, ". Computed as floor(", amount, " * ", piece3_bps, " / 10000) in ", C, " minor units"); pattern: "^[1-9][0-9]{0,17}$"; optional: true; }
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
when_eq(payer_fee_kind, percent) { serviceFeeAmount { type: money<C>; description: concat("Machine-owned ", payer_fee, " service fee computed from ", amount, "; charged on top at funding"); pattern: "^[1-9][0-9]{0,17}$"; optional: true; } }
|
|
198
|
-
when_eq(payer_fee_kind, binding) { [payer_fee] { type: payer_fee; description: concat("Immutable exact ", payer, " fee in ", C, " minor units; charged on top at funding"); pattern: "^[1-9][0-9]{0,17}$"; } }
|
|
199
|
-
when(release_deadline) {
|
|
200
|
-
when_not(release_to) { [release_deadline] { type: date; description: concat("The date an undecided hold releases to the ", words(payee), " on; ", release, " and cancellation decide only before it"); } }
|
|
201
|
-
when(release_to) { [release_deadline] { type: date; description: concat("The date an undecided hold releases to the ", words(release_to), " on; ", release, " and cancellation decide only before it"); } }
|
|
202
|
-
}
|
|
203
|
-
when(derived_amount) { [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; } }
|
|
204
|
-
}
|
|
205
|
-
lifecycle {
|
|
206
|
-
when_not(payee_fee) {
|
|
207
|
-
when_not(cancel_splits) {
|
|
208
|
-
states created funded disputed released;
|
|
209
|
-
when(on_cancel) { states cancelled; }
|
|
210
|
-
when(has_cancel_quote) { states cancellation_quoted cancelled settled; }
|
|
211
|
-
states abandoned;
|
|
212
|
-
}
|
|
213
|
-
when(cancel_splits) { states created funding_1 funded disputed releasing_1 released cancelling_1 cancelled abandoned; }
|
|
214
|
-
}
|
|
215
|
-
when(payee_fee) {
|
|
216
|
-
when_not(on_cancel) { states created funding_1 funded disputed releasing_1 released abandoned; }
|
|
217
|
-
when(on_cancel) {
|
|
218
|
-
when_eq(payer_fee_kind, percent) { states created funding_1 funding_2 funding_3 funded disputed releasing_1 releasing_2 released cancelling_1 cancelling_2 cancelled abandoning_1 abandoned abandoning_2; }
|
|
219
|
-
when_eq(payer_fee_kind, none) { states created funding_1 funding_2 funded disputed releasing_1 releasing_2 released cancelling_1 cancelling_2 cancelled abandoning_1 abandoned; }
|
|
220
|
-
when_eq(payer_fee_kind, binding) { states created funding_1 funding_2 funded disputed releasing_1 releasing_2 released cancelling_1 cancelling_2 cancelled abandoning_1 abandoned; }
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
initial created;
|
|
224
|
-
when_not(payee_fee) {
|
|
225
|
-
when_not(cancel_splits) {
|
|
226
|
-
on fund_piece_1: created -> funded;
|
|
227
|
-
when_not(has_cancel_quote) {
|
|
228
|
-
on [release_name]: funded -> released;
|
|
229
|
-
when(release_deadline) { on release_on_deadline: funded -> released; }
|
|
230
|
-
}
|
|
231
|
-
when(has_cancel_quote) {
|
|
232
|
-
on [release_name]: funded | cancellation_quoted -> released;
|
|
233
|
-
when(release_deadline) { on release_on_deadline: funded | cancellation_quoted -> released; }
|
|
234
|
-
on quote_cancellation: funded | cancellation_quoted -> cancellation_quoted;
|
|
235
|
-
on cancel: cancellation_quoted -> cancelled;
|
|
236
|
-
on retain_cancellation_charge: cancelled -> settled;
|
|
237
|
-
}
|
|
238
|
-
when(on_cancel) { on cancel: funded -> cancelled; }
|
|
239
|
-
}
|
|
240
|
-
when(cancel_splits) {
|
|
241
|
-
on fund_piece_1: created -> funding_1;
|
|
242
|
-
on fund_piece_2: funding_1 -> funded;
|
|
243
|
-
on [release_name]: funded -> releasing_1;
|
|
244
|
-
on release_piece_2: releasing_1 -> released;
|
|
245
|
-
on cancel: funded -> cancelling_1;
|
|
246
|
-
on refund_piece_2: cancelling_1 -> cancelled;
|
|
247
|
-
on unfund_piece_1: funding_1 -> abandoned;
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
when(payee_fee) {
|
|
251
|
-
when_not(on_cancel) {
|
|
252
|
-
on fund_piece_1: created -> funding_1;
|
|
253
|
-
on fund_piece_2: funding_1 -> funded;
|
|
254
|
-
on [release_name]: funded -> releasing_1;
|
|
255
|
-
when(release_deadline) { on release_on_deadline: funded -> releasing_1; }
|
|
256
|
-
on release_piece_2: releasing_1 -> released;
|
|
257
|
-
on unfund_piece_1: funding_1 -> abandoned;
|
|
258
|
-
}
|
|
259
|
-
when(on_cancel) {
|
|
260
|
-
on fund_piece_1: created -> funding_1;
|
|
261
|
-
on fund_piece_2: funding_1 -> funding_2;
|
|
262
|
-
when_eq(payer_fee_kind, percent) {
|
|
263
|
-
on fund_piece_3: funding_2 -> funding_3;
|
|
264
|
-
on collect_service_fee: funding_3 -> funded;
|
|
265
|
-
}
|
|
266
|
-
when_eq(payer_fee_kind, none) { on fund_piece_3: funding_2 -> funded; }
|
|
267
|
-
when_eq(payer_fee_kind, binding) { on fund_piece_3: funding_2 -> funded; }
|
|
268
|
-
on [release_name]: funded -> releasing_1;
|
|
269
|
-
on release_piece_2: releasing_1 -> releasing_2;
|
|
270
|
-
on release_piece_3: releasing_2 -> released;
|
|
271
|
-
on cancel: funded -> cancelling_1;
|
|
272
|
-
on refund_piece_2: cancelling_1 -> cancelling_2;
|
|
273
|
-
on refund_piece_3: cancelling_2 -> cancelled;
|
|
274
|
-
when_eq(payer_fee_kind, percent) {
|
|
275
|
-
on unfund_piece_3: funding_3 -> abandoning_2;
|
|
276
|
-
on unfund_piece_2: funding_2 | abandoning_2 -> abandoning_1;
|
|
277
|
-
}
|
|
278
|
-
when_eq(payer_fee_kind, none) { on unfund_piece_2: funding_2 -> abandoning_1; }
|
|
279
|
-
when_eq(payer_fee_kind, binding) { on unfund_piece_2: funding_2 -> abandoning_1; }
|
|
280
|
-
on unfund_piece_1: funding_1 | abandoning_1 -> abandoned;
|
|
281
|
-
}
|
|
282
|
-
}
|
|
283
|
-
when_not(has_cancel_quote) {
|
|
284
|
-
on dispute: funded -> disputed;
|
|
285
|
-
}
|
|
286
|
-
when(has_cancel_quote) {
|
|
287
|
-
on dispute: funded | cancellation_quoted -> disputed;
|
|
288
|
-
}
|
|
289
|
-
on resume: disputed -> funded;
|
|
290
|
-
on abandon: created -> abandoned;
|
|
291
|
-
}
|
|
292
|
-
parties {
|
|
293
|
-
payer: payer;
|
|
294
|
-
beneficiary: payee;
|
|
295
|
-
}
|
|
296
|
-
when_eq(payer_fee_kind, percent) {
|
|
297
|
-
computes fees: { amount_field: serviceFeeAmount; base_field: amount; bearer_field: payer_account_field; position: on_top; rule: { kind: bps; bps: payer_bps; }; };
|
|
298
|
-
}
|
|
299
|
-
when_eq(payer_fee_kind, binding) {
|
|
300
|
-
computes fees: { amount_field: camel(payer_fee); base_field: amount; bearer_field: payer_account_field; position: on_top; rule: { kind: exact; field: camel(payer_fee); currency_field: currency; }; };
|
|
301
|
-
}
|
|
302
|
-
when(payee_fee) {
|
|
303
|
-
when(on_cancel) {
|
|
304
|
-
action fund_piece_2 {
|
|
305
|
-
when(private_actions) { public: none; }
|
|
306
|
-
let(metadata_instrument): "metadata.instrumentId";
|
|
307
|
-
let(metadata_instance): "metadata.instrumentInstanceId";
|
|
308
|
-
let(metadata_phase): "metadata.phase";
|
|
309
|
-
agent_description: concat("Move piece 2 of the split amount from the ", words(payer), " into escrow. Piece 1 must be in escrow first. It debits the ", words(payer), " for real, and the hold is still short of funded until piece 3 lands.");
|
|
310
|
-
summary: "Fund piece 2 of the held amount into escrow";
|
|
311
|
-
moves: [{ key: transfer; operation: internal_transfer.create; bind: { amount: { from: instance; path: fields.piece2Amount; }; currency: { from: instance; path: fields.currency; }; destinationAccountId: { from: instance; path: refs.escrowAccountId; }; [metadata_instrument]: { from: const; value: instrument; }; [metadata_instance]: { from: instance; path: instrumentInstanceId; }; [metadata_phase]: { from: const; value: fund_piece_2; }; productId: { from: instance; path: productId; }; sourceAccountId: { from: instance; path: concat("fields.", payer_account_field); }; }; capture: { fundPiece2TransferTransferId: transferId; }; }];
|
|
312
|
-
sandbox_failure_point: funding;
|
|
313
|
-
steps: [];
|
|
314
|
-
}
|
|
315
|
-
action fund_piece_3 {
|
|
316
|
-
when(private_actions) { public: none; }
|
|
317
|
-
let(metadata_instrument): "metadata.instrumentId";
|
|
318
|
-
let(metadata_instance): "metadata.instrumentInstanceId";
|
|
319
|
-
let(metadata_phase): "metadata.phase";
|
|
320
|
-
agent_description: concat("Move piece 3 of the split amount from the ", words(payer), " into escrow. Pieces 1 and 2 must be in escrow first. It debits the ", words(payer), " for real. Where the product charges a percentage payer fee, one more call collects that fee before the hold counts as funded.");
|
|
321
|
-
summary: "Fund piece 3 of the held amount into escrow";
|
|
322
|
-
moves: [{ key: transfer; operation: internal_transfer.create; bind: { amount: { from: instance; path: fields.piece3Amount; }; currency: { from: instance; path: fields.currency; }; destinationAccountId: { from: instance; path: refs.escrowAccountId; }; [metadata_instrument]: { from: const; value: instrument; }; [metadata_instance]: { from: instance; path: instrumentInstanceId; }; [metadata_phase]: { from: const; value: fund_piece_3; }; productId: { from: instance; path: productId; }; sourceAccountId: { from: instance; path: concat("fields.", payer_account_field); }; }; capture: { fundPiece3TransferTransferId: transferId; }; }];
|
|
323
|
-
sandbox_failure_point: funding;
|
|
324
|
-
steps: [];
|
|
325
|
-
}
|
|
326
|
-
when_eq(payer_fee_kind, percent) {
|
|
327
|
-
action collect_service_fee {
|
|
328
|
-
when(private_actions) { public: none; }
|
|
329
|
-
let(metadata_instrument): "metadata.instrumentId";
|
|
330
|
-
let(metadata_instance): "metadata.instrumentInstanceId";
|
|
331
|
-
let(metadata_phase): "metadata.phase";
|
|
332
|
-
agent_description: concat("Charge the ", words(payer), " the percentage service fee on top of the held amount and pay it to the platform account. All three funding pieces must be in escrow first. It debits the ", words(payer), " for real and it is the last step of funding.");
|
|
333
|
-
summary: "Collect the payer-side service fee";
|
|
334
|
-
moves: [{ key: transfer; operation: internal_transfer.create; bind: { amount: { from: instance; path: fields.serviceFeeAmount; }; 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: collect_service_fee; }; productId: { from: instance; path: productId; }; sourceAccountId: { from: instance; path: concat("fields.", payer_account_field); }; }; capture: { collectServiceFeeTransferTransferId: transferId; }; }];
|
|
335
|
-
steps: [];
|
|
336
|
-
}
|
|
337
|
-
}
|
|
338
|
-
action release_piece_2 {
|
|
339
|
-
when(private_actions) { public: none; }
|
|
340
|
-
let(metadata_instrument): "metadata.instrumentId";
|
|
341
|
-
let(metadata_instance): "metadata.instrumentInstanceId";
|
|
342
|
-
let(metadata_phase): "metadata.phase";
|
|
343
|
-
agent_description: concat("Pay piece 2 out of escrow once the release decision has run. Its destination follows the product fee split, either the platform account or the ", words(payee), ". Money leaves escrow on this call and does not come back.");
|
|
344
|
-
summary: "Release piece 2 of the held amount";
|
|
345
|
-
when(carved_spans) { let(release_piece_2_destination): "fields.platformAccountId"; }
|
|
346
|
-
when_not(carved_spans) { let(release_piece_2_destination): concat("fields.", payee_account_field); }
|
|
347
|
-
moves: [{ key: transfer; operation: internal_transfer.create; bind: { amount: { from: instance; path: fields.piece2Amount; }; currency: { from: instance; path: fields.currency; }; destinationAccountId: { from: instance; path: release_piece_2_destination; }; [metadata_instrument]: { from: const; value: instrument; }; [metadata_instance]: { from: instance; path: instrumentInstanceId; }; [metadata_phase]: { from: const; value: release_piece_2; }; productId: { from: instance; path: productId; }; sourceAccountId: { from: instance; path: refs.escrowAccountId; }; }; capture: { releasePiece2TransferTransferId: transferId; }; }];
|
|
348
|
-
steps: [];
|
|
349
|
-
}
|
|
350
|
-
action release_piece_3 {
|
|
351
|
-
when(private_actions) { public: none; }
|
|
352
|
-
let(metadata_instrument): "metadata.instrumentId";
|
|
353
|
-
let(metadata_instance): "metadata.instrumentInstanceId";
|
|
354
|
-
let(metadata_phase): "metadata.phase";
|
|
355
|
-
agent_description: concat("Pay piece 3 out of escrow to the platform account, which closes the release. Piece 2 must be released first. Money leaves escrow on this call and does not come back.");
|
|
356
|
-
summary: "Release piece 3 of the held amount";
|
|
357
|
-
moves: [{ key: transfer; operation: internal_transfer.create; bind: { amount: { from: instance; path: fields.piece3Amount; }; 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: release_piece_3; }; productId: { from: instance; path: productId; }; sourceAccountId: { from: instance; path: refs.escrowAccountId; }; }; capture: { releasePiece3TransferTransferId: transferId; }; }];
|
|
358
|
-
steps: [];
|
|
359
|
-
}
|
|
360
|
-
action refund_piece_2 {
|
|
361
|
-
when(private_actions) { public: none; }
|
|
362
|
-
let(metadata_instrument): "metadata.instrumentId";
|
|
363
|
-
let(metadata_instance): "metadata.instrumentInstanceId";
|
|
364
|
-
let(metadata_phase): "metadata.phase";
|
|
365
|
-
agent_description: concat("Return piece 2 out of escrow once cancel has run. Its destination follows the product fee split, either the ", words(payer), " or the ", words(payee), ". Money leaves escrow on this call and does not come back.");
|
|
366
|
-
summary: "Return piece 2 on cancellation";
|
|
367
|
-
when(carved_spans) { let(refund_piece_2_destination): concat("fields.", payer_account_field); }
|
|
368
|
-
when_not(carved_spans) { let(refund_piece_2_destination): concat("fields.", payee_account_field); }
|
|
369
|
-
moves: [{ key: transfer; operation: internal_transfer.create; bind: { amount: { from: instance; path: fields.piece2Amount; }; currency: { from: instance; path: fields.currency; }; destinationAccountId: { from: instance; path: refund_piece_2_destination; }; [metadata_instrument]: { from: const; value: instrument; }; [metadata_instance]: { from: instance; path: instrumentInstanceId; }; [metadata_phase]: { from: const; value: refund_piece_2; }; productId: { from: instance; path: productId; }; sourceAccountId: { from: instance; path: refs.escrowAccountId; }; }; capture: { refundPiece2TransferTransferId: transferId; }; }];
|
|
370
|
-
steps: [];
|
|
371
|
-
}
|
|
372
|
-
action refund_piece_3 {
|
|
373
|
-
when(private_actions) { public: none; }
|
|
374
|
-
let(metadata_instrument): "metadata.instrumentId";
|
|
375
|
-
let(metadata_instance): "metadata.instrumentInstanceId";
|
|
376
|
-
let(metadata_phase): "metadata.phase";
|
|
377
|
-
agent_description: concat("Return piece 3 out of escrow to the ", words(payee), ", which closes the cancellation. Piece 2 must be returned first. Money leaves escrow on this call and does not come back.");
|
|
378
|
-
summary: "Return piece 3 on cancellation";
|
|
379
|
-
moves: [{ key: transfer; operation: internal_transfer.create; bind: { amount: { from: instance; path: fields.piece3Amount; }; currency: { from: instance; path: fields.currency; }; destinationAccountId: { from: instance; path: concat("fields.", payee_account_field); }; [metadata_instrument]: { from: const; value: instrument; }; [metadata_instance]: { from: instance; path: instrumentInstanceId; }; [metadata_phase]: { from: const; value: refund_piece_3; }; productId: { from: instance; path: productId; }; sourceAccountId: { from: instance; path: refs.escrowAccountId; }; }; capture: { refundPiece3TransferTransferId: transferId; }; }];
|
|
380
|
-
steps: [];
|
|
381
|
-
}
|
|
382
|
-
action unfund_piece_1 {
|
|
383
|
-
when(private_actions) { public: none; }
|
|
384
|
-
let(metadata_instrument): "metadata.instrumentId";
|
|
385
|
-
let(metadata_instance): "metadata.instrumentInstanceId";
|
|
386
|
-
let(metadata_phase): "metadata.phase";
|
|
387
|
-
agent_description: concat("Return piece 1 from escrow to the ", words(payer), " and abandon the ", words(instrument), ". It runs when only piece 1 was ever funded, or after the later pieces have already gone back. The settlement ends abandoned.");
|
|
388
|
-
summary: "Return piece 1 to the {payer} on abandonment";
|
|
389
|
-
moves: [{ key: transfer; operation: internal_transfer.create; bind: { amount: { from: instance; path: fields.piece1Amount; }; currency: { from: instance; path: fields.currency; }; destinationAccountId: { from: instance; path: concat("fields.", payer_account_field); }; [metadata_instrument]: { from: const; value: instrument; }; [metadata_instance]: { from: instance; path: instrumentInstanceId; }; [metadata_phase]: { from: const; value: unfund_piece_1; }; productId: { from: instance; path: productId; }; sourceAccountId: { from: instance; path: refs.escrowAccountId; }; }; capture: { unfundPiece1TransferTransferId: transferId; }; }];
|
|
390
|
-
steps: [];
|
|
391
|
-
}
|
|
392
|
-
action unfund_piece_2 {
|
|
393
|
-
when(private_actions) { public: none; }
|
|
394
|
-
let(metadata_instrument): "metadata.instrumentId";
|
|
395
|
-
let(metadata_instance): "metadata.instrumentInstanceId";
|
|
396
|
-
let(metadata_phase): "metadata.phase";
|
|
397
|
-
agent_description: concat("Return piece 2 from escrow to the ", words(payer), " while abandoning a part-funded ", words(instrument), ". Funding must have stopped at piece 2, or piece 3 must already be back. Piece 1 still has to be returned after this.");
|
|
398
|
-
summary: "Return piece 2 to the {payer} on abandonment";
|
|
399
|
-
moves: [{ key: transfer; operation: internal_transfer.create; bind: { amount: { from: instance; path: fields.piece2Amount; }; currency: { from: instance; path: fields.currency; }; destinationAccountId: { from: instance; path: concat("fields.", payer_account_field); }; [metadata_instrument]: { from: const; value: instrument; }; [metadata_instance]: { from: instance; path: instrumentInstanceId; }; [metadata_phase]: { from: const; value: unfund_piece_2; }; productId: { from: instance; path: productId; }; sourceAccountId: { from: instance; path: refs.escrowAccountId; }; }; capture: { unfundPiece2TransferTransferId: transferId; }; }];
|
|
400
|
-
steps: [];
|
|
401
|
-
}
|
|
402
|
-
when_eq(payer_fee_kind, percent) {
|
|
403
|
-
action unfund_piece_3 {
|
|
404
|
-
when(private_actions) { public: none; }
|
|
405
|
-
let(metadata_instrument): "metadata.instrumentId";
|
|
406
|
-
let(metadata_instance): "metadata.instrumentInstanceId";
|
|
407
|
-
let(metadata_phase): "metadata.phase";
|
|
408
|
-
agent_description: concat("Return piece 3 from escrow to the ", words(payer), " while abandoning a ", words(instrument), " funded as far as piece 3. Pieces 2 and 1 still have to be returned after this, in that order.");
|
|
409
|
-
summary: "Return piece 3 to the {payer} on abandonment";
|
|
410
|
-
moves: [{ key: transfer; operation: internal_transfer.create; bind: { amount: { from: instance; path: fields.piece3Amount; }; currency: { from: instance; path: fields.currency; }; destinationAccountId: { from: instance; path: concat("fields.", payer_account_field); }; [metadata_instrument]: { from: const; value: instrument; }; [metadata_instance]: { from: instance; path: instrumentInstanceId; }; [metadata_phase]: { from: const; value: unfund_piece_3; }; productId: { from: instance; path: productId; }; sourceAccountId: { from: instance; path: refs.escrowAccountId; }; }; capture: { unfundPiece3TransferTransferId: transferId; }; }];
|
|
411
|
-
steps: [];
|
|
412
|
-
}
|
|
413
|
-
}
|
|
414
|
-
}
|
|
415
|
-
when_not(on_cancel) {
|
|
416
|
-
when_eq(payee_fee_kind, percent) {
|
|
417
|
-
computes fees: { amount_field: piece2Amount; base_field: amount; bearer_field: payee_account_field; position: carved; rule: { kind: bps; bps: payee_bps; }; };
|
|
418
|
-
partitions: { total_field: amount; piece_fields: [piece1Amount, piece2Amount]; };
|
|
419
|
-
}
|
|
420
|
-
when_eq(payee_fee_kind, binding) {
|
|
421
|
-
computes fees: { amount_field: camel(payee_fee); base_field: amount; bearer_field: payee_account_field; position: carved; rule: { kind: exact; field: camel(payee_fee); currency_field: currency; }; };
|
|
422
|
-
partitions: { total_field: amount; piece_fields: [piece1Amount, camel(payee_fee)]; };
|
|
423
|
-
}
|
|
424
|
-
}
|
|
425
|
-
when(on_cancel) {
|
|
426
|
-
when_eq(payee_fee_kind, percent) {
|
|
427
|
-
when(carved_spans) {
|
|
428
|
-
computes fees: { amount_field: carvedFeeAmount; base_field: amount; bearer_field: payee_account_field; position: carved; rule: { kind: bps; bps: payee_bps; }; };
|
|
429
|
-
}
|
|
430
|
-
when_not(carved_spans) {
|
|
431
|
-
computes fees: { amount_field: piece3Amount; base_field: amount; bearer_field: payee_account_field; position: carved; rule: { kind: bps; bps: payee_bps; }; };
|
|
432
|
-
}
|
|
433
|
-
}
|
|
434
|
-
partitions: { total_field: amount; piece_fields: [piece1Amount, piece2Amount, piece3Amount]; };
|
|
435
|
-
when(carved_spans) { partitions: { total_field: carvedFeeAmount; piece_fields: [piece2Amount, piece3Amount]; }; }
|
|
436
|
-
}
|
|
437
|
-
}
|
|
438
|
-
when_not(payee_fee) {
|
|
439
|
-
when(cancel_splits) { partitions: { total_field: amount; piece_fields: [piece1Amount, piece2Amount]; }; }
|
|
440
|
-
}
|
|
441
|
-
when(derived_amount) {
|
|
442
|
-
computes derived: { field: derived_field; source_field: derived_source; rounding: floor; rule: { kind: percentage_of; bps: derived_rule; }; };
|
|
443
|
-
}
|
|
444
|
-
action create {
|
|
445
|
-
when(private_actions) { public: none; }
|
|
446
|
-
let(owner_id): "owner.id";
|
|
447
|
-
let(owner_type): "owner.type";
|
|
448
|
-
agent_description: concat("Open the ", words(instrument), " and provision the escrow account that holds the funds. This is the first call, and every funding and release action moves money through that account. It does not move the held amount itself.");
|
|
449
|
-
summary: concat("Create a ", words(instrument), " settlement");
|
|
450
|
-
when_not(derived_amount) { moves: []; }
|
|
451
|
-
when(derived_amount) {
|
|
452
|
-
let(metadata_instrument): "metadata.instrumentId";
|
|
453
|
-
let(metadata_instance): "metadata.instrumentInstanceId";
|
|
454
|
-
let(metadata_phase): "metadata.phase";
|
|
455
|
-
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; }; }];
|
|
456
|
-
}
|
|
457
|
-
steps: [{ operation: "account.escrow.provision"; bind: { currency: { from: instance; path: fields.currency; }; [owner_id]: { from: instance; path: productId; }; [owner_type]: { from: const; value: product; }; productId: { from: instance; path: productId; }; role: { from: const; value: product_escrow; }; }; capture: { escrowAccountId: accountId; }; }];
|
|
458
|
-
}
|
|
459
|
-
action fund_piece_1 {
|
|
460
|
-
when(private_actions) { public: none; }
|
|
461
|
-
let(metadata_instrument): "metadata.instrumentId";
|
|
462
|
-
let(metadata_instance): "metadata.instrumentInstanceId";
|
|
463
|
-
let(metadata_phase): "metadata.phase";
|
|
464
|
-
agent_description: concat("Move the ", words(payer), "'s money into this settlement's escrow. Call it after create and before any release. It debits the ", words(payer), " for real, and where the product charges a payer-side fee that fee is taken on top in the same call. Only a later release or unwind moves the money again.");
|
|
465
|
-
summary: "Fund piece 1 of the held amount into escrow";
|
|
466
|
-
when_not(payee_fee) {
|
|
467
|
-
when_not(cancel_splits) { moves: [{ key: transfer; operation: internal_transfer.create; bind: { amount: { from: instance; path: concat("fields.", amount); }; currency: { from: instance; path: fields.currency; }; destinationAccountId: { from: instance; path: refs.escrowAccountId; }; [metadata_instrument]: { from: const; value: instrument; }; [metadata_instance]: { from: instance; path: instrumentInstanceId; }; [metadata_phase]: { from: const; value: fund_piece_1; }; productId: { from: instance; path: productId; }; sourceAccountId: { from: instance; path: concat("fields.", payer_account_field); }; }; capture: { fundPiece1TransferTransferId: transferId; }; }]; }
|
|
468
|
-
when(cancel_splits) { moves: [{ key: transfer; operation: internal_transfer.create; bind: { amount: { from: instance; path: fields.piece1Amount; }; currency: { from: instance; path: fields.currency; }; destinationAccountId: { from: instance; path: refs.escrowAccountId; }; [metadata_instrument]: { from: const; value: instrument; }; [metadata_instance]: { from: instance; path: instrumentInstanceId; }; [metadata_phase]: { from: const; value: fund_piece_1; }; productId: { from: instance; path: productId; }; sourceAccountId: { from: instance; path: concat("fields.", payer_account_field); }; }; capture: { fundPiece1TransferTransferId: transferId; }; }]; }
|
|
469
|
-
}
|
|
470
|
-
when(payee_fee) { moves: [{ key: transfer; operation: internal_transfer.create; bind: { amount: { from: instance; path: fields.piece1Amount; }; currency: { from: instance; path: fields.currency; }; destinationAccountId: { from: instance; path: refs.escrowAccountId; }; [metadata_instrument]: { from: const; value: instrument; }; [metadata_instance]: { from: instance; path: instrumentInstanceId; }; [metadata_phase]: { from: const; value: fund_piece_1; }; productId: { from: instance; path: productId; }; sourceAccountId: { from: instance; path: concat("fields.", payer_account_field); }; }; capture: { fundPiece1TransferTransferId: transferId; }; }]; }
|
|
471
|
-
when_eq(payer_fee_kind, percent) {
|
|
472
|
-
when_not(payee_fee) { moves: [{ key: service_fee; operation: internal_transfer.create; bind: { amount: { from: instance; path: fields.serviceFeeAmount; }; 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: fund_piece_1; }; productId: { from: instance; path: productId; }; sourceAccountId: { from: instance; path: concat("fields.", payer_account_field); }; }; capture: { fundPiece1ServiceFeeTransferId: transferId; }; }]; }
|
|
473
|
-
when(payee_fee) {
|
|
474
|
-
when_not(on_cancel) { moves: [{ key: service_fee; operation: internal_transfer.create; bind: { amount: { from: instance; path: fields.serviceFeeAmount; }; 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: fund_piece_1; }; productId: { from: instance; path: productId; }; sourceAccountId: { from: instance; path: concat("fields.", payer_account_field); }; }; capture: { fundPiece1ServiceFeeTransferId: transferId; }; }]; }
|
|
475
|
-
}
|
|
476
|
-
}
|
|
477
|
-
when_eq(payer_fee_kind, binding) { moves: [{ key: service_fee; operation: internal_transfer.create; bind: { amount: { from: instance; path: concat("fields.", payer_fee); }; 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: fund_piece_1; }; productId: { from: instance; path: productId; }; sourceAccountId: { from: instance; path: concat("fields.", payer_account_field); }; }; capture: { fundPiece1ServiceFeeTransferId: transferId; }; }]; }
|
|
478
|
-
sandbox_failure_point: funding;
|
|
479
|
-
steps: [];
|
|
480
|
-
}
|
|
481
|
-
action [release_name] {
|
|
482
|
-
when(private_actions) { public: none; }
|
|
483
|
-
let(metadata_instrument): "metadata.instrumentId";
|
|
484
|
-
let(metadata_instance): "metadata.instrumentInstanceId";
|
|
485
|
-
let(metadata_phase): "metadata.phase";
|
|
486
|
-
let(release_capture): camel(concat(release_name, "_transfer_transfer_id"));
|
|
487
|
-
agent_description: concat("Confirm ", release, " and start paying the held money out of escrow. The ", words(instrument), " must be funded first, and the caller must be ", release_actor_text, ". Where the product requires a decision reference, supply it here. Money leaves escrow on this call and does not come back.");
|
|
488
|
-
summary: concat("Confirm through ", release, " and start the release payout");
|
|
489
|
-
when(release_deadline) { deadline { field: release_deadline; }; }
|
|
490
|
-
when(release_fields) {
|
|
491
|
-
input {
|
|
492
|
-
type: object;
|
|
493
|
-
additional_properties: false;
|
|
494
|
-
properties {
|
|
495
|
-
for input_name in keys(release_fields) {
|
|
496
|
-
[input_name]: get(release_fields, input_name);
|
|
497
|
-
}
|
|
498
|
-
}
|
|
499
|
-
required: keys(release_fields);
|
|
500
|
-
}
|
|
501
|
-
}
|
|
502
|
-
port { allowed_parties: release_allowed; };
|
|
503
|
-
when_not(payee_fee) {
|
|
504
|
-
when_not(cancel_splits) {
|
|
505
|
-
when_not(release_to) { moves: [{ key: transfer; operation: internal_transfer.create; bind: { amount: { from: instance; path: concat("fields.", amount); }; currency: { from: instance; path: fields.currency; }; destinationAccountId: { from: instance; path: concat("fields.", payee_account_field); }; [metadata_instrument]: { from: const; value: instrument; }; [metadata_instance]: { from: instance; path: instrumentInstanceId; }; [metadata_phase]: { from: const; value: release_name; }; productId: { from: instance; path: productId; }; sourceAccountId: { from: instance; path: refs.escrowAccountId; }; }; capture: { [release_capture]: transferId; }; }]; }
|
|
506
|
-
when(release_to) { moves: [{ key: transfer; operation: internal_transfer.create; bind: { amount: { from: instance; path: concat("fields.", amount); }; currency: { from: instance; path: fields.currency; }; destinationAccountId: { from: instance; path: concat("fields.", release_to_account_field); }; [metadata_instrument]: { from: const; value: instrument; }; [metadata_instance]: { from: instance; path: instrumentInstanceId; }; [metadata_phase]: { from: const; value: release_name; }; productId: { from: instance; path: productId; }; sourceAccountId: { from: instance; path: refs.escrowAccountId; }; }; capture: { [release_capture]: transferId; }; }]; }
|
|
507
|
-
}
|
|
508
|
-
when(cancel_splits) { moves: [{ key: transfer; operation: internal_transfer.create; bind: { amount: { from: instance; path: fields.piece1Amount; }; currency: { from: instance; path: fields.currency; }; destinationAccountId: { from: instance; path: concat("fields.", payee_account_field); }; [metadata_instrument]: { from: const; value: instrument; }; [metadata_instance]: { from: instance; path: instrumentInstanceId; }; [metadata_phase]: { from: const; value: release_name; }; productId: { from: instance; path: productId; }; sourceAccountId: { from: instance; path: refs.escrowAccountId; }; }; capture: { [release_capture]: transferId; }; }]; }
|
|
509
|
-
}
|
|
510
|
-
when(payee_fee) { moves: [{ key: transfer; operation: internal_transfer.create; bind: { amount: { from: instance; path: fields.piece1Amount; }; currency: { from: instance; path: fields.currency; }; destinationAccountId: { from: instance; path: concat("fields.", payee_account_field); }; [metadata_instrument]: { from: const; value: instrument; }; [metadata_instance]: { from: instance; path: instrumentInstanceId; }; [metadata_phase]: { from: const; value: release_name; }; productId: { from: instance; path: productId; }; sourceAccountId: { from: instance; path: refs.escrowAccountId; }; }; capture: { [release_capture]: transferId; }; }]; }
|
|
511
|
-
sandbox_failure_point: "release";
|
|
512
|
-
steps: [];
|
|
513
|
-
}
|
|
514
|
-
when(release_deadline) {
|
|
515
|
-
action release_on_deadline {
|
|
516
|
-
when(private_actions) { public: none; }
|
|
517
|
-
let(metadata_instrument): "metadata.instrumentId";
|
|
518
|
-
let(metadata_instance): "metadata.instrumentInstanceId";
|
|
519
|
-
let(metadata_phase): "metadata.phase";
|
|
520
|
-
when_not(release_to) { summary: "Release to the {payee} when {release_deadline} arrives undecided"; }
|
|
521
|
-
when(release_to) { summary: "Release to the {release_to} when {release_deadline} arrives undecided"; }
|
|
522
|
-
due { field: release_deadline; };
|
|
523
|
-
when_not(payee_fee) {
|
|
524
|
-
when_not(release_to) { moves: [{ key: transfer; operation: internal_transfer.create; bind: { amount: { from: instance; path: concat("fields.", amount); }; currency: { from: instance; path: fields.currency; }; destinationAccountId: { from: instance; path: concat("fields.", payee_account_field); }; [metadata_instrument]: { from: const; value: instrument; }; [metadata_instance]: { from: instance; path: instrumentInstanceId; }; [metadata_phase]: { from: const; value: release_on_deadline; }; productId: { from: instance; path: productId; }; sourceAccountId: { from: instance; path: refs.escrowAccountId; }; }; capture: { releaseOnDeadlineTransferTransferId: transferId; }; }]; }
|
|
525
|
-
when(release_to) { moves: [{ key: transfer; operation: internal_transfer.create; bind: { amount: { from: instance; path: concat("fields.", amount); }; currency: { from: instance; path: fields.currency; }; destinationAccountId: { from: instance; path: concat("fields.", release_to_account_field); }; [metadata_instrument]: { from: const; value: instrument; }; [metadata_instance]: { from: instance; path: instrumentInstanceId; }; [metadata_phase]: { from: const; value: release_on_deadline; }; productId: { from: instance; path: productId; }; sourceAccountId: { from: instance; path: refs.escrowAccountId; }; }; capture: { releaseOnDeadlineTransferTransferId: transferId; }; }]; }
|
|
526
|
-
}
|
|
527
|
-
when(payee_fee) { moves: [{ key: transfer; operation: internal_transfer.create; bind: { amount: { from: instance; path: fields.piece1Amount; }; currency: { from: instance; path: fields.currency; }; destinationAccountId: { from: instance; path: concat("fields.", payee_account_field); }; [metadata_instrument]: { from: const; value: instrument; }; [metadata_instance]: { from: instance; path: instrumentInstanceId; }; [metadata_phase]: { from: const; value: release_on_deadline; }; productId: { from: instance; path: productId; }; sourceAccountId: { from: instance; path: refs.escrowAccountId; }; }; capture: { releaseOnDeadlineTransferTransferId: transferId; }; }]; }
|
|
528
|
-
steps: [];
|
|
529
|
-
}
|
|
530
|
-
}
|
|
531
|
-
when(on_cancel) {
|
|
532
|
-
action cancel {
|
|
533
|
-
when(private_actions) { public: none; }
|
|
534
|
-
let(metadata_instrument): "metadata.instrumentId";
|
|
535
|
-
let(metadata_instance): "metadata.instrumentInstanceId";
|
|
536
|
-
let(metadata_phase): "metadata.phase";
|
|
537
|
-
agent_description: concat("Cancel the ", words(instrument), " and start returning the held money to the ", words(payer), ". It must be funded and not yet released. Cancellation is final, and a cancelled settlement never releases. Where the product sets a release deadline, this call is refused once that date passes.");
|
|
538
|
-
summary: "Cancel the settlement and start the unwind";
|
|
539
|
-
when(release_deadline) { deadline { field: release_deadline; }; }
|
|
540
|
-
when_not(payee_fee) {
|
|
541
|
-
when_not(cancel_splits) { moves: [{ key: transfer; operation: internal_transfer.create; bind: { amount: { from: instance; path: concat("fields.", amount); }; currency: { from: instance; path: fields.currency; }; destinationAccountId: { from: instance; path: concat("fields.", payer_account_field); }; [metadata_instrument]: { from: const; value: instrument; }; [metadata_instance]: { from: instance; path: instrumentInstanceId; }; [metadata_phase]: { from: const; value: cancel; }; productId: { from: instance; path: productId; }; sourceAccountId: { from: instance; path: refs.escrowAccountId; }; }; capture: { cancelTransferTransferId: transferId; }; }]; }
|
|
542
|
-
when(cancel_splits) { moves: [{ key: transfer; operation: internal_transfer.create; bind: { amount: { from: instance; path: fields.piece1Amount; }; currency: { from: instance; path: fields.currency; }; destinationAccountId: { from: instance; path: concat("fields.", payer_account_field); }; [metadata_instrument]: { from: const; value: instrument; }; [metadata_instance]: { from: instance; path: instrumentInstanceId; }; [metadata_phase]: { from: const; value: cancel; }; productId: { from: instance; path: productId; }; sourceAccountId: { from: instance; path: refs.escrowAccountId; }; }; capture: { cancelTransferTransferId: transferId; }; }]; }
|
|
543
|
-
}
|
|
544
|
-
when(payee_fee) { moves: [{ key: transfer; operation: internal_transfer.create; bind: { amount: { from: instance; path: fields.piece1Amount; }; currency: { from: instance; path: fields.currency; }; destinationAccountId: { from: instance; path: concat("fields.", payer_account_field); }; [metadata_instrument]: { from: const; value: instrument; }; [metadata_instance]: { from: instance; path: instrumentInstanceId; }; [metadata_phase]: { from: const; value: cancel; }; productId: { from: instance; path: productId; }; sourceAccountId: { from: instance; path: refs.escrowAccountId; }; }; capture: { cancelTransferTransferId: transferId; }; }]; }
|
|
545
|
-
steps: [];
|
|
546
|
-
}
|
|
547
|
-
}
|
|
548
|
-
when(has_cancel_quote) {
|
|
549
|
-
action quote_cancellation {
|
|
550
|
-
when(private_actions) { public: none; }
|
|
551
|
-
agent_description: concat("Price the cancellation and hold that price open. No money moves. The charge the ", words(payee), " keeps and the refund the ", words(payer), " gets back are worked out here and frozen for ", cancel_offer_life, ", after which a fresh call prices it again. Call cancel to spend the quote.");
|
|
552
|
-
summary: "Price the cancellation and hold that price open";
|
|
553
|
-
when(release_deadline) { deadline { field: release_deadline; }; }
|
|
554
|
-
quote {
|
|
555
|
-
baseField: amount;
|
|
556
|
-
chargeRef: cancellationChargeAmount;
|
|
557
|
-
charges: [{ bps: cancel_charge_bps; }];
|
|
558
|
-
expires { offset: cancel_offer_life; }
|
|
559
|
-
fixes: [amount, payer_account_field];
|
|
560
|
-
netDestinationField: payer_account_field;
|
|
561
|
-
netRef: cancellationRefundAmount;
|
|
562
|
-
}
|
|
563
|
-
moves: [];
|
|
564
|
-
steps: [];
|
|
565
|
-
}
|
|
566
|
-
action cancel {
|
|
567
|
-
when(private_actions) { public: none; }
|
|
568
|
-
let(metadata_instrument): "metadata.instrumentId";
|
|
569
|
-
let(metadata_instance): "metadata.instrumentInstanceId";
|
|
570
|
-
let(metadata_phase): "metadata.phase";
|
|
571
|
-
agent_description: concat("Spend the frozen quote and return the net refund to the ", words(payer), ". This moves money and does not reverse. It needs an un-expired quote from quote_cancellation, and the charge that refund left behind goes to the ", words(payee), " through retain_cancellation_charge.");
|
|
572
|
-
summary: concat("Spend the quoted cancellation and return the net to the ", words(payer));
|
|
573
|
-
commit: quote_cancellation;
|
|
574
|
-
when(release_deadline) { deadline { field: release_deadline; }; }
|
|
575
|
-
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.", payer_account_field); }; [metadata_instrument]: { from: const; value: instrument; }; [metadata_instance]: { from: instance; path: instrumentInstanceId; }; [metadata_phase]: { from: const; value: cancel; }; productId: { from: instance; path: productId; }; sourceAccountId: { from: instance; path: refs.escrowAccountId; }; }; capture: { cancelTransferTransferId: transferId; }; }];
|
|
576
|
-
steps: [];
|
|
577
|
-
}
|
|
578
|
-
action retain_cancellation_charge {
|
|
579
|
-
when(private_actions) { public: none; }
|
|
580
|
-
let(metadata_instrument): "metadata.instrumentId";
|
|
581
|
-
let(metadata_instance): "metadata.instrumentInstanceId";
|
|
582
|
-
let(metadata_phase): "metadata.phase";
|
|
583
|
-
agent_description: concat("Pay the quoted cancellation charge out of escrow to the ", words(payee), ". This moves money and does not reverse. The settlement must already be cancelled, so the figure is the one the quote fixed and the escrow ends empty.");
|
|
584
|
-
summary: concat("Pay the quoted cancellation charge to the ", words(payee));
|
|
585
|
-
when(has_charge) {
|
|
586
|
-
moves: [{ key: charge; operation: internal_transfer.create; bind: { amount: { from: instance; path: refs.cancellationChargeAmount; }; currency: { from: instance; path: fields.currency; }; destinationAccountId: { from: instance; path: concat("fields.", payee_account_field); }; [metadata_instrument]: { from: const; value: instrument; }; [metadata_instance]: { from: instance; path: instrumentInstanceId; }; [metadata_phase]: { from: const; value: retain_cancellation_charge; }; productId: { from: instance; path: productId; }; sourceAccountId: { from: instance; path: refs.escrowAccountId; }; }; capture: { retainCancellationChargeTransferId: transferId; }; }];
|
|
587
|
-
}
|
|
588
|
-
when_not(has_charge) {
|
|
589
|
-
moves: [];
|
|
590
|
-
}
|
|
591
|
-
steps: [];
|
|
592
|
-
}
|
|
593
|
-
}
|
|
594
|
-
when(payee_fee) {
|
|
595
|
-
when_not(on_cancel) {
|
|
596
|
-
action fund_piece_2 {
|
|
597
|
-
when(private_actions) { public: none; }
|
|
598
|
-
let(metadata_instrument): "metadata.instrumentId";
|
|
599
|
-
let(metadata_instance): "metadata.instrumentInstanceId";
|
|
600
|
-
let(metadata_phase): "metadata.phase";
|
|
601
|
-
agent_description: concat("Move the carved platform fee piece from the ", words(payer), " into escrow, which completes funding. Piece 1 must be in escrow first. It debits the ", words(payer), " for real, and the whole held amount then sits in escrow.");
|
|
602
|
-
summary: "Fund piece 2 of the held amount into escrow";
|
|
603
|
-
when_eq(payee_fee_kind, percent) { moves: [{ key: transfer; operation: internal_transfer.create; bind: { amount: { from: instance; path: fields.piece2Amount; }; currency: { from: instance; path: fields.currency; }; destinationAccountId: { from: instance; path: refs.escrowAccountId; }; [metadata_instrument]: { from: const; value: instrument; }; [metadata_instance]: { from: instance; path: instrumentInstanceId; }; [metadata_phase]: { from: const; value: fund_piece_2; }; productId: { from: instance; path: productId; }; sourceAccountId: { from: instance; path: concat("fields.", payer_account_field); }; }; capture: { fundPiece2TransferTransferId: transferId; }; }]; }
|
|
604
|
-
when_eq(payee_fee_kind, binding) { moves: [{ key: transfer; operation: internal_transfer.create; bind: { amount: { from: instance; path: concat("fields.", payee_fee); }; currency: { from: instance; path: fields.currency; }; destinationAccountId: { from: instance; path: refs.escrowAccountId; }; [metadata_instrument]: { from: const; value: instrument; }; [metadata_instance]: { from: instance; path: instrumentInstanceId; }; [metadata_phase]: { from: const; value: fund_piece_2; }; productId: { from: instance; path: productId; }; sourceAccountId: { from: instance; path: concat("fields.", payer_account_field); }; }; capture: { fundPiece2TransferTransferId: transferId; }; }]; }
|
|
605
|
-
sandbox_failure_point: funding;
|
|
606
|
-
steps: [];
|
|
607
|
-
}
|
|
608
|
-
action release_piece_2 {
|
|
609
|
-
when(private_actions) { public: none; }
|
|
610
|
-
let(metadata_instrument): "metadata.instrumentId";
|
|
611
|
-
let(metadata_instance): "metadata.instrumentInstanceId";
|
|
612
|
-
let(metadata_phase): "metadata.phase";
|
|
613
|
-
agent_description: concat("Pay the carved platform fee out of escrow to the platform account, which closes the release. The release decision must have run first. Money leaves escrow on this call and does not come back.");
|
|
614
|
-
summary: "Release piece 2 of the held amount";
|
|
615
|
-
when_eq(payee_fee_kind, percent) { moves: [{ key: transfer; operation: internal_transfer.create; bind: { amount: { from: instance; path: fields.piece2Amount; }; 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: release_piece_2; }; productId: { from: instance; path: productId; }; sourceAccountId: { from: instance; path: refs.escrowAccountId; }; }; capture: { releasePiece2TransferTransferId: transferId; }; }]; }
|
|
616
|
-
when_eq(payee_fee_kind, binding) { moves: [{ key: transfer; operation: internal_transfer.create; bind: { amount: { from: instance; path: concat("fields.", payee_fee); }; 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: release_piece_2; }; productId: { from: instance; path: productId; }; sourceAccountId: { from: instance; path: refs.escrowAccountId; }; }; capture: { releasePiece2TransferTransferId: transferId; }; }]; }
|
|
617
|
-
steps: [];
|
|
618
|
-
}
|
|
619
|
-
action unfund_piece_1 {
|
|
620
|
-
when(private_actions) { public: none; }
|
|
621
|
-
let(metadata_instrument): "metadata.instrumentId";
|
|
622
|
-
let(metadata_instance): "metadata.instrumentInstanceId";
|
|
623
|
-
let(metadata_phase): "metadata.phase";
|
|
624
|
-
agent_description: concat("Return piece 1 from escrow to the ", words(payer), " and abandon the ", words(instrument), ". It runs only while funding stopped after piece 1, before the release. The settlement ends abandoned.");
|
|
625
|
-
summary: "Return piece 1 to the {payer} on abandonment";
|
|
626
|
-
moves: [{ key: transfer; operation: internal_transfer.create; bind: { amount: { from: instance; path: fields.piece1Amount; }; currency: { from: instance; path: fields.currency; }; destinationAccountId: { from: instance; path: concat("fields.", payer_account_field); }; [metadata_instrument]: { from: const; value: instrument; }; [metadata_instance]: { from: instance; path: instrumentInstanceId; }; [metadata_phase]: { from: const; value: unfund_piece_1; }; productId: { from: instance; path: productId; }; sourceAccountId: { from: instance; path: refs.escrowAccountId; }; }; capture: { unfundPiece1TransferTransferId: transferId; }; }];
|
|
627
|
-
steps: [];
|
|
628
|
-
}
|
|
629
|
-
}
|
|
630
|
-
}
|
|
631
|
-
when_not(payee_fee) {
|
|
632
|
-
when(cancel_splits) {
|
|
633
|
-
action fund_piece_2 {
|
|
634
|
-
when(private_actions) { public: none; }
|
|
635
|
-
let(metadata_instrument): "metadata.instrumentId";
|
|
636
|
-
let(metadata_instance): "metadata.instrumentInstanceId";
|
|
637
|
-
let(metadata_phase): "metadata.phase";
|
|
638
|
-
agent_description: concat("Move piece 2 of the split amount from the ", words(payer), " into escrow, which completes funding. Piece 1 must be in escrow first. It debits the ", words(payer), " for real, and the whole held amount then sits in escrow.");
|
|
639
|
-
summary: "Fund piece 2 of the held amount into escrow";
|
|
640
|
-
moves: [{ key: transfer; operation: internal_transfer.create; bind: { amount: { from: instance; path: fields.piece2Amount; }; currency: { from: instance; path: fields.currency; }; destinationAccountId: { from: instance; path: refs.escrowAccountId; }; [metadata_instrument]: { from: const; value: instrument; }; [metadata_instance]: { from: instance; path: instrumentInstanceId; }; [metadata_phase]: { from: const; value: fund_piece_2; }; productId: { from: instance; path: productId; }; sourceAccountId: { from: instance; path: concat("fields.", payer_account_field); }; }; capture: { fundPiece2TransferTransferId: transferId; }; }];
|
|
641
|
-
sandbox_failure_point: funding;
|
|
642
|
-
steps: [];
|
|
643
|
-
}
|
|
644
|
-
action release_piece_2 {
|
|
645
|
-
when(private_actions) { public: none; }
|
|
646
|
-
let(metadata_instrument): "metadata.instrumentId";
|
|
647
|
-
let(metadata_instance): "metadata.instrumentInstanceId";
|
|
648
|
-
let(metadata_phase): "metadata.phase";
|
|
649
|
-
agent_description: concat("Pay piece 2 out of escrow to the ", words(payee), ", which closes the release. The release decision must have run first. Money leaves escrow on this call and does not come back.");
|
|
650
|
-
summary: "Release piece 2 of the held amount";
|
|
651
|
-
moves: [{ key: transfer; operation: internal_transfer.create; bind: { amount: { from: instance; path: fields.piece2Amount; }; currency: { from: instance; path: fields.currency; }; destinationAccountId: { from: instance; path: concat("fields.", payee_account_field); }; [metadata_instrument]: { from: const; value: instrument; }; [metadata_instance]: { from: instance; path: instrumentInstanceId; }; [metadata_phase]: { from: const; value: release_piece_2; }; productId: { from: instance; path: productId; }; sourceAccountId: { from: instance; path: refs.escrowAccountId; }; }; capture: { releasePiece2TransferTransferId: transferId; }; }];
|
|
652
|
-
steps: [];
|
|
653
|
-
}
|
|
654
|
-
action refund_piece_2 {
|
|
655
|
-
when(private_actions) { public: none; }
|
|
656
|
-
let(metadata_instrument): "metadata.instrumentId";
|
|
657
|
-
let(metadata_instance): "metadata.instrumentInstanceId";
|
|
658
|
-
let(metadata_phase): "metadata.phase";
|
|
659
|
-
agent_description: concat("Return piece 2 out of escrow to the ", words(payee), ", which closes the cancellation. Cancel must have run first. Money leaves escrow on this call and does not come back.");
|
|
660
|
-
summary: "Return piece 2 on cancellation";
|
|
661
|
-
moves: [{ key: transfer; operation: internal_transfer.create; bind: { amount: { from: instance; path: fields.piece2Amount; }; currency: { from: instance; path: fields.currency; }; destinationAccountId: { from: instance; path: concat("fields.", payee_account_field); }; [metadata_instrument]: { from: const; value: instrument; }; [metadata_instance]: { from: instance; path: instrumentInstanceId; }; [metadata_phase]: { from: const; value: refund_piece_2; }; productId: { from: instance; path: productId; }; sourceAccountId: { from: instance; path: refs.escrowAccountId; }; }; capture: { refundPiece2TransferTransferId: transferId; }; }];
|
|
662
|
-
steps: [];
|
|
663
|
-
}
|
|
664
|
-
action unfund_piece_1 {
|
|
665
|
-
when(private_actions) { public: none; }
|
|
666
|
-
let(metadata_instrument): "metadata.instrumentId";
|
|
667
|
-
let(metadata_instance): "metadata.instrumentInstanceId";
|
|
668
|
-
let(metadata_phase): "metadata.phase";
|
|
669
|
-
agent_description: concat("Return piece 1 from escrow to the ", words(payer), " and abandon the ", words(instrument), ". It runs only while funding stopped after piece 1, before the release. The settlement ends abandoned.");
|
|
670
|
-
summary: "Return piece 1 to the {payer} on abandonment";
|
|
671
|
-
moves: [{ key: transfer; operation: internal_transfer.create; bind: { amount: { from: instance; path: fields.piece1Amount; }; currency: { from: instance; path: fields.currency; }; destinationAccountId: { from: instance; path: concat("fields.", payer_account_field); }; [metadata_instrument]: { from: const; value: instrument; }; [metadata_instance]: { from: instance; path: instrumentInstanceId; }; [metadata_phase]: { from: const; value: unfund_piece_1; }; productId: { from: instance; path: productId; }; sourceAccountId: { from: instance; path: refs.escrowAccountId; }; }; capture: { unfundPiece1TransferTransferId: transferId; }; }];
|
|
672
|
-
steps: [];
|
|
673
|
-
}
|
|
674
|
-
}
|
|
675
|
-
}
|
|
676
|
-
action abandon {
|
|
677
|
-
when(private_actions) { public: none; }
|
|
678
|
-
agent_description: concat("Close a ", words(instrument), " that was never funded. The escrow account must be empty, so this refuses once any piece is in it. It moves no money and the settlement ends here with no way back.");
|
|
679
|
-
summary: "Abandon the settlement before any money is held";
|
|
680
|
-
requires drained: { path: refs.escrowAccountId; };
|
|
681
|
-
moves: [];
|
|
682
|
-
steps: [];
|
|
683
|
-
}
|
|
684
|
-
action dispute {
|
|
685
|
-
when(private_actions) { public: none; }
|
|
686
|
-
agent_description: concat("Freeze a funded ", words(instrument), " so neither release nor refund can run until the parties settle the disagreement. No money moves and the whole balance stays put. Only a funded ", words(instrument), " disputes.");
|
|
687
|
-
description: concat("Freezes funded ", words(instrument), ", preserving the entire balance while the parties decide what happens next.");
|
|
688
|
-
summary: concat("Dispute a ", words(instrument), " settlement");
|
|
689
|
-
when(release_deadline) { deadline { field: release_deadline; }; }
|
|
690
|
-
moves: [];
|
|
691
|
-
steps: [];
|
|
692
|
-
}
|
|
693
|
-
action resume {
|
|
694
|
-
when(private_actions) { public: none; }
|
|
695
|
-
agent_description: concat("Lift the dispute hold and put the ", words(instrument), " back where it was, funded and ready to release. No money moves and the balance was never touched. Only a disputed ", words(instrument), " resumes. Use this when the parties settled without a refund.");
|
|
696
|
-
description: concat("Ends the dispute hold without moving money and returns the fully funded ", words(instrument), " to its first milestone.");
|
|
697
|
-
summary: concat("Resume a ", words(instrument), " settlement");
|
|
698
|
-
moves: [];
|
|
699
|
-
steps: [];
|
|
700
|
-
}
|
|
701
|
-
}
|
|
702
|
-
when(whole_amount) {
|
|
703
|
-
let(fee_collection_key): "x-hyperscale-fee-collection-port";
|
|
704
|
-
let(reference_filter_key): "x-hyperscale-reference-filter";
|
|
705
|
-
let(whole_fee_field): camel(whole_fee);
|
|
706
|
-
title: sentence(instrument);
|
|
707
|
-
id_prefix: if_eq(id_prefix_override, false, prefix(instrument), id_prefix_override);
|
|
708
|
-
summary: concat("Escrow-held payment from ", words(payer), " to ", words(payee));
|
|
709
|
-
description: concat("Held payment: the ", words(payer), " funds ", amount, " into this settlement's own escrow; ", release_actor_text, " confirms through ", release, " to release");
|
|
710
|
-
distinct_parties: true;
|
|
711
|
-
caller_parked_states: {
|
|
712
|
-
created: "The tenant either funds the hold or abandons it before any money moves.";
|
|
713
|
-
disputed: "Dispute resolution is a human judgment; the parties resume the hold to continue.";
|
|
714
|
-
};
|
|
715
|
-
fields {
|
|
716
|
-
currency { type: text; description: "One currency for the hold and its fee"; const: C; }
|
|
717
|
-
amount { type: money<C>; description: "Amount held in custody, in {C} minor units"; }
|
|
718
|
-
[payee]AccountId { type: account<C>; description: "Party the hold protects and pays on release"; [reference_filter_key]: { column: role; values: [customer_balance]; }; }
|
|
719
|
-
[payer]AccountId { type: account<C>; description: "Party whose money is held until the hold resolves"; [reference_filter_key]: { column: role; values: [customer_balance]; }; }
|
|
720
|
-
platformAccountId { type: account<C>; description: "Product revenue or customer balance account collecting the custody fee"; [fee_collection_key]: true; [reference_filter_key]: { column: role; values: [customer_balance, product_revenue]; }; }
|
|
721
|
-
when(reference) { reference { type: text; description: "The tenant's own identifier for what is held: invoice number, project code, contract reference"; optional: true; max_length: 180; min_length: 1; } }
|
|
722
|
-
[release_deadline] { type: date; description: "Date the hold releases to the payee on its own; fixed at creation and the cutoff for both the release port and cancellation"; }
|
|
723
|
-
whole_fee { type: money<C>; description: "Custody fee charged on top at funding and never placed in escrow"; }
|
|
724
|
-
when(upstream) { upstream { type: ref<instrument>; description: "Hold this one is carved out of, when a chain of holds passes the same money down"; optional: true; pattern: concat("^", id_prefix_override, "_(sandbox|live)_[a-z0-9]{8,64}$"); } }
|
|
725
|
-
}
|
|
726
|
-
lifecycle {
|
|
727
|
-
states created funded disputed released cancelled abandoned;
|
|
728
|
-
initial created;
|
|
729
|
-
on abandon: created -> abandoned;
|
|
730
|
-
on cancel: funded -> cancelled;
|
|
731
|
-
on dispute: funded -> disputed;
|
|
732
|
-
on fund: created -> funded;
|
|
733
|
-
on [release_action]: funded -> released;
|
|
734
|
-
on release_on_deadline: funded -> released;
|
|
735
|
-
on resume: disputed -> funded;
|
|
736
|
-
}
|
|
737
|
-
parties { beneficiary: payee_account_field; payer: payer_account_field; }
|
|
738
|
-
required: [payer_account_field, payee_account_field, platformAccountId, amount, whole_fee_field, currency, release_deadline];
|
|
739
|
-
action create {
|
|
740
|
-
when(private_actions) { public: none; }
|
|
741
|
-
let(owner_id): "owner.id";
|
|
742
|
-
let(owner_type): "owner.type";
|
|
743
|
-
agent_description: concat("Open the ", words(instrument), " and provision the escrow account that holds the money. No money moves on this call. ", release_deadline, " is fixed here and never moves again, so one date governs the early release, the cancellation, and the automatic payout.");
|
|
744
|
-
summary: concat("Create a ", words(instrument), " settlement");
|
|
745
|
-
moves: [];
|
|
746
|
-
steps: [{ operation: "account.escrow.provision"; bind: { currency: { from: instance; path: fields.currency; }; [owner_id]: { from: instance; path: productId; }; [owner_type]: { from: const; value: product; }; productId: { from: instance; path: productId; }; role: { from: const; value: product_escrow; }; }; capture: { escrowAccountId: accountId; }; }];
|
|
747
|
-
}
|
|
748
|
-
action fund {
|
|
749
|
-
when(private_actions) { public: none; }
|
|
750
|
-
agent_description: concat("Move ", amount, " from the ", words(payer), " into escrow and charge ", whole_fee_field, " on top to the platform account, as one linked batch. This moves money and the ", words(payer), " pays both. Only a created hold funds. The fee never enters escrow, so it is non-refundable however the hold ends.");
|
|
751
|
-
summary: "Fund the hold and collect the custody fee";
|
|
752
|
-
moves: [
|
|
753
|
-
{ key: principal; operation: internal_transfer.create; bind: { amount: { from: instance; path: concat("fields.", amount); }; currency: { from: instance; path: fields.currency; }; destinationAccountId: { from: instance; path: refs.escrowAccountId; }; "metadata.instrumentId": { from: const; value: instrument; }; "metadata.instrumentInstanceId": { from: instance; path: instrumentInstanceId; }; "metadata.phase": { from: const; value: fund; }; productId: { from: instance; path: productId; }; sourceAccountId: { from: instance; path: concat("fields.", payer_account_field); }; }; capture: { fundingTransferId: transferId; }; },
|
|
754
|
-
{ key: service_fee; operation: internal_transfer.create; bind: { amount: { from: instance; path: concat("fields.", whole_fee_field); }; currency: { from: instance; path: fields.currency; }; destinationAccountId: { from: instance; path: fields.platformAccountId; }; "metadata.instrumentId": { from: const; value: instrument; }; "metadata.instrumentInstanceId": { from: instance; path: instrumentInstanceId; }; "metadata.phase": { from: const; value: service_fee; }; productId: { from: instance; path: productId; }; sourceAccountId: { from: instance; path: concat("fields.", payer_account_field); }; }; capture: { serviceFeeTransferId: transferId; }; }
|
|
755
|
-
];
|
|
756
|
-
sandbox_failure_point: funding;
|
|
757
|
-
steps: [];
|
|
758
|
-
}
|
|
759
|
-
action [release_action] {
|
|
760
|
-
when(private_actions) { public: none; }
|
|
761
|
-
agent_description: concat("Pay the ", words(payee), " the whole ", amount, " out of escrow ahead of ", release_deadline, ". This moves money and does not reverse. Only a funded hold releases, the caller must act as ", words(at(release_allowed, 1)), ", and the call is refused once ", release_deadline, " passes. The custody fee stays posted.");
|
|
762
|
-
summary: concat("Release the hold to the ", words(payee), " early");
|
|
763
|
-
deadline: { field: release_deadline; };
|
|
764
|
-
port: { allowed_parties: [at(release_allowed, 1)]; };
|
|
765
|
-
moves: [{ key: release_action; operation: internal_transfer.create; bind: { amount: { from: instance; path: concat("fields.", amount); }; currency: { from: instance; path: fields.currency; }; destinationAccountId: { from: instance; path: concat("fields.", payee_account_field); }; "metadata.instrumentId": { from: const; value: instrument; }; "metadata.instrumentInstanceId": { from: instance; path: instrumentInstanceId; }; "metadata.phase": { from: const; value: release_action; }; productId: { from: instance; path: productId; }; sourceAccountId: { from: instance; path: refs.escrowAccountId; }; }; capture: { releaseTransferId: transferId; }; }];
|
|
766
|
-
sandbox_failure_point: "release";
|
|
767
|
-
steps: [];
|
|
768
|
-
}
|
|
769
|
-
action release_on_deadline {
|
|
770
|
-
when(private_actions) { public: none; }
|
|
771
|
-
summary: concat("Release the hold to the ", words(payee), " on its release date");
|
|
772
|
-
due: { field: release_deadline; };
|
|
773
|
-
event_name: concat(instrument, ".released_on_deadline");
|
|
774
|
-
moves: [{ key: release_action; operation: internal_transfer.create; bind: { amount: { from: instance; path: concat("fields.", amount); }; currency: { from: instance; path: fields.currency; }; destinationAccountId: { from: instance; path: concat("fields.", payee_account_field); }; "metadata.instrumentId": { from: const; value: instrument; }; "metadata.instrumentInstanceId": { from: instance; path: instrumentInstanceId; }; "metadata.phase": { from: const; value: deadline_release; }; productId: { from: instance; path: productId; }; sourceAccountId: { from: instance; path: refs.escrowAccountId; }; }; capture: { deadlineReleaseTransferId: transferId; }; }];
|
|
775
|
-
steps: [];
|
|
776
|
-
}
|
|
777
|
-
action cancel {
|
|
778
|
-
when(private_actions) { public: none; }
|
|
779
|
-
agent_description: concat("Return the whole ", amount, " from escrow to the ", words(payer), " and end the hold. This moves money and does not reverse. Only a funded hold cancels, and only before ", release_deadline, " passes. The custody fee stays posted because the custody it paid for was already provided.");
|
|
780
|
-
summary: "Cancel the hold and return the held amount";
|
|
781
|
-
deadline: { field: release_deadline; };
|
|
782
|
-
event_name: concat(instrument, ".cancelled");
|
|
783
|
-
moves: [{ key: refund; operation: internal_transfer.create; bind: { amount: { from: instance; path: concat("fields.", amount); }; currency: { from: instance; path: fields.currency; }; destinationAccountId: { from: instance; path: concat("fields.", payer_account_field); }; "metadata.instrumentId": { from: const; value: instrument; }; "metadata.instrumentInstanceId": { from: instance; path: instrumentInstanceId; }; "metadata.phase": { from: const; value: cancel_refund; }; productId: { from: instance; path: productId; }; sourceAccountId: { from: instance; path: refs.escrowAccountId; }; }; capture: { cancelRefundTransferId: transferId; }; }];
|
|
784
|
-
steps: [];
|
|
785
|
-
}
|
|
786
|
-
action abandon {
|
|
787
|
-
when(private_actions) { public: none; }
|
|
788
|
-
agent_description: concat("Close a ", words(instrument), " that was opened and never funded. No money has moved and the escrow must be empty, so this is a lifecycle transition only. A funded hold refuses. Release it to the ", words(payee), " or cancel it back to the ", words(payer), " instead.");
|
|
789
|
-
summary: "Abandon the hold before it is funded";
|
|
790
|
-
moves: [];
|
|
791
|
-
requires_drained: { path: refs.escrowAccountId; };
|
|
792
|
-
steps: [];
|
|
793
|
-
}
|
|
794
|
-
action dispute {
|
|
795
|
-
when(private_actions) { public: none; }
|
|
796
|
-
agent_description: concat("Freeze a funded ", words(instrument), " so neither release nor refund can run until the parties settle the disagreement. No money moves and the whole balance stays put. Only a funded ", words(instrument), " disputes.");
|
|
797
|
-
description: concat("Freezes funded ", words(instrument), ", preserving the entire balance while the parties decide what happens next.");
|
|
798
|
-
summary: concat("Dispute a ", words(instrument), " settlement");
|
|
799
|
-
deadline: { field: release_deadline; };
|
|
800
|
-
moves: [];
|
|
801
|
-
steps: [];
|
|
802
|
-
}
|
|
803
|
-
action resume {
|
|
804
|
-
when(private_actions) { public: none; }
|
|
805
|
-
agent_description: concat("Lift the dispute hold and put the ", words(instrument), " back where it was, funded and ready to release. No money moves and the balance was never touched. Only a disputed ", words(instrument), " resumes. Use this when the parties settled without a refund.");
|
|
806
|
-
description: concat("Ends the dispute hold without moving money and returns the fully funded ", words(instrument), " to its first milestone.");
|
|
807
|
-
summary: concat("Resume a ", words(instrument), " settlement");
|
|
808
|
-
moves: [];
|
|
809
|
-
steps: [];
|
|
810
|
-
}
|
|
811
|
-
}
|
|
812
|
-
}
|