xytara 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +3 -0
- package/LICENSE +201 -0
- package/README.md +257 -0
- package/adapters/AUTHORING_GUIDE.md +89 -0
- package/adapters/ONBOARDING_POSTURE.md +39 -0
- package/adapters/adapter-manifest.schema.json +209 -0
- package/adapters/examples/example-execution-adapter.js +61 -0
- package/adapters/examples/example-execution-adapter.manifest.json +62 -0
- package/adapters/examples/example-settlement-adapter.js +55 -0
- package/adapters/examples/example-settlement-adapter.manifest.json +59 -0
- package/adapters/interfaces.js +56 -0
- package/client.js +3 -0
- package/fixtures/catalog.js +728 -0
- package/index.js +25 -0
- package/lib/catalog_contract.js +141 -0
- package/lib/catalog_views.js +36 -0
- package/lib/command_flow.js +42 -0
- package/lib/commerce_artifacts.js +294 -0
- package/lib/commerce_cases.js +303 -0
- package/lib/commerce_client.js +5373 -0
- package/lib/commerce_quotes.js +113 -0
- package/lib/commerce_reports.js +14315 -0
- package/lib/commerce_runtime.js +215 -0
- package/lib/http_transport.js +40 -0
- package/lib/record_queries.js +47 -0
- package/lib/resource_views.js +31 -0
- package/lib/route_matchers.js +34 -0
- package/lib/route_params.js +19 -0
- package/package.json +55 -0
- package/server.js +626 -0
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const crypto = require("crypto");
|
|
4
|
+
|
|
5
|
+
function setResultRemediation(state, transactionId, patch) {
|
|
6
|
+
if (!transactionId || !state.transactions.has(transactionId)) {
|
|
7
|
+
return;
|
|
8
|
+
}
|
|
9
|
+
const current = state.transactions.get(transactionId);
|
|
10
|
+
const currentTransactionRemediation = current.transaction && current.transaction.remediation && typeof current.transaction.remediation === "object"
|
|
11
|
+
? current.transaction.remediation
|
|
12
|
+
: {};
|
|
13
|
+
const currentReceiptRemediation = current.receipt && current.receipt.remediation && typeof current.receipt.remediation === "object"
|
|
14
|
+
? current.receipt.remediation
|
|
15
|
+
: {};
|
|
16
|
+
const nextRemediation = {
|
|
17
|
+
...currentTransactionRemediation,
|
|
18
|
+
...currentReceiptRemediation,
|
|
19
|
+
...(patch || {}),
|
|
20
|
+
updated_at_iso: new Date().toISOString()
|
|
21
|
+
};
|
|
22
|
+
current.transaction.remediation = nextRemediation;
|
|
23
|
+
current.receipt.remediation = nextRemediation;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function appendHistory(record, event, body, nextStatus) {
|
|
27
|
+
const timestamp = new Date().toISOString();
|
|
28
|
+
const actor = body && body.actor && typeof body.actor === "object" ? body.actor : { type: "operator", role: "reviewer" };
|
|
29
|
+
const context = body && body.context && typeof body.context === "object" ? body.context : null;
|
|
30
|
+
const previousStatus = record.status || null;
|
|
31
|
+
return {
|
|
32
|
+
...record,
|
|
33
|
+
status: nextStatus || record.status,
|
|
34
|
+
updated_at_iso: timestamp,
|
|
35
|
+
history: [
|
|
36
|
+
...(Array.isArray(record.history) ? record.history : []),
|
|
37
|
+
{
|
|
38
|
+
at_iso: timestamp,
|
|
39
|
+
event,
|
|
40
|
+
note: body && typeof body.note === "string" ? body.note : null,
|
|
41
|
+
reason_code: body && typeof body.reason_code === "string" ? body.reason_code : record.reason_code || null,
|
|
42
|
+
action_code: body && typeof body.action_code === "string" ? body.action_code : null,
|
|
43
|
+
previous_status: previousStatus,
|
|
44
|
+
next_status: nextStatus || record.status,
|
|
45
|
+
actor,
|
|
46
|
+
context
|
|
47
|
+
}
|
|
48
|
+
].slice(-20)
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function createDispute(state, body) {
|
|
53
|
+
const timestamp = new Date().toISOString();
|
|
54
|
+
const actor = body && body.actor && typeof body.actor === "object" ? body.actor : { type: "operator", role: "creator" };
|
|
55
|
+
const context = body && body.context && typeof body.context === "object" ? body.context : null;
|
|
56
|
+
const dispute = {
|
|
57
|
+
dispute_id: `dsp_${crypto.randomBytes(6).toString("hex")}`,
|
|
58
|
+
title: typeof body.title === "string" && body.title.trim() ? body.title.trim() : "Dispute",
|
|
59
|
+
description: typeof body.description === "string" && body.description.trim() ? body.description.trim() : null,
|
|
60
|
+
status: "open",
|
|
61
|
+
transaction_id: body.transaction_id || null,
|
|
62
|
+
receipt_id: body.receipt_id || null,
|
|
63
|
+
reason_code: body.reason_code || "other",
|
|
64
|
+
note: body.note || null,
|
|
65
|
+
evidence: body && body.evidence && typeof body.evidence === "object" ? body.evidence : {},
|
|
66
|
+
created_at_iso: timestamp,
|
|
67
|
+
updated_at_iso: timestamp,
|
|
68
|
+
created_actor: actor,
|
|
69
|
+
created_context: context,
|
|
70
|
+
history: [
|
|
71
|
+
{
|
|
72
|
+
at_iso: timestamp,
|
|
73
|
+
event: "opened",
|
|
74
|
+
note: body.note || body.description || null,
|
|
75
|
+
reason_code: body.reason_code || "other",
|
|
76
|
+
action_code: null,
|
|
77
|
+
previous_status: null,
|
|
78
|
+
next_status: "open",
|
|
79
|
+
actor,
|
|
80
|
+
context
|
|
81
|
+
}
|
|
82
|
+
]
|
|
83
|
+
};
|
|
84
|
+
state.disputes.set(dispute.dispute_id, dispute);
|
|
85
|
+
setResultRemediation(state, dispute.transaction_id, {
|
|
86
|
+
status: "under_dispute",
|
|
87
|
+
reason_code: dispute.reason_code,
|
|
88
|
+
active_case_type: "dispute",
|
|
89
|
+
active_case_id: dispute.dispute_id,
|
|
90
|
+
compensating_action_status: "none"
|
|
91
|
+
});
|
|
92
|
+
return dispute;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function createRefund(state, body) {
|
|
96
|
+
const timestamp = new Date().toISOString();
|
|
97
|
+
const actor = body && body.actor && typeof body.actor === "object" ? body.actor : { type: "operator", role: "creator" };
|
|
98
|
+
const context = body && body.context && typeof body.context === "object" ? body.context : null;
|
|
99
|
+
const refund = {
|
|
100
|
+
refund_id: `rfd_${crypto.randomBytes(6).toString("hex")}`,
|
|
101
|
+
status: "requested",
|
|
102
|
+
transaction_id: body.transaction_id || null,
|
|
103
|
+
receipt_id: body.receipt_id || null,
|
|
104
|
+
reason_code: body.reason_code || "other",
|
|
105
|
+
note: body.note || null,
|
|
106
|
+
requested_by: typeof body.requested_by === "string" && body.requested_by.trim() ? body.requested_by.trim() : "operator",
|
|
107
|
+
created_at_iso: timestamp,
|
|
108
|
+
updated_at_iso: timestamp,
|
|
109
|
+
created_actor: actor,
|
|
110
|
+
created_context: context,
|
|
111
|
+
history: [
|
|
112
|
+
{
|
|
113
|
+
at_iso: timestamp,
|
|
114
|
+
event: "requested",
|
|
115
|
+
note: body.note || null,
|
|
116
|
+
reason_code: body.reason_code || "other",
|
|
117
|
+
action_code: null,
|
|
118
|
+
previous_status: null,
|
|
119
|
+
next_status: "requested",
|
|
120
|
+
actor,
|
|
121
|
+
context
|
|
122
|
+
}
|
|
123
|
+
]
|
|
124
|
+
};
|
|
125
|
+
state.refunds.set(refund.refund_id, refund);
|
|
126
|
+
setResultRemediation(state, refund.transaction_id, {
|
|
127
|
+
status: "remediation_pending",
|
|
128
|
+
reason_code: refund.reason_code,
|
|
129
|
+
active_case_type: "refund",
|
|
130
|
+
active_case_id: refund.refund_id,
|
|
131
|
+
compensating_action_status: "none"
|
|
132
|
+
});
|
|
133
|
+
return refund;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function actOnDispute(state, disputeId, body) {
|
|
137
|
+
const current = state.disputes.get(disputeId);
|
|
138
|
+
if (!current) return null;
|
|
139
|
+
const action = String((body && body.action) || "").trim();
|
|
140
|
+
const statusByAction = {
|
|
141
|
+
review: "under_review",
|
|
142
|
+
escalate: "escalated",
|
|
143
|
+
reopen: "open",
|
|
144
|
+
resolve: "resolved",
|
|
145
|
+
dismiss: "dismissed"
|
|
146
|
+
};
|
|
147
|
+
const nextStatus = statusByAction[action] || current.status;
|
|
148
|
+
const next = appendHistory(current, action || "review", body, nextStatus);
|
|
149
|
+
if (body && typeof body.resolution_code === "string" && body.resolution_code.trim()) {
|
|
150
|
+
next.resolution_code = body.resolution_code.trim();
|
|
151
|
+
}
|
|
152
|
+
state.disputes.set(disputeId, next);
|
|
153
|
+
const remediationStatus = next.status === "resolved" || next.status === "dismissed"
|
|
154
|
+
? "none"
|
|
155
|
+
: next.status === "escalated"
|
|
156
|
+
? "escalated"
|
|
157
|
+
: "under_dispute";
|
|
158
|
+
setResultRemediation(state, next.transaction_id, {
|
|
159
|
+
status: remediationStatus,
|
|
160
|
+
reason_code: remediationStatus === "none" ? null : next.reason_code,
|
|
161
|
+
active_case_type: remediationStatus === "none" ? null : "dispute",
|
|
162
|
+
active_case_id: remediationStatus === "none" ? null : next.dispute_id,
|
|
163
|
+
compensating_action_status: remediationStatus === "none" ? null : "none"
|
|
164
|
+
});
|
|
165
|
+
return next;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function actOnRefund(state, refundId, body) {
|
|
169
|
+
const current = state.refunds.get(refundId);
|
|
170
|
+
if (!current) return null;
|
|
171
|
+
const action = String((body && body.action) || "").trim();
|
|
172
|
+
const statusByAction = {
|
|
173
|
+
review: "under_review",
|
|
174
|
+
approve: "approved",
|
|
175
|
+
complete: "completed",
|
|
176
|
+
cancel: "cancelled",
|
|
177
|
+
fail: "failed",
|
|
178
|
+
pending_manual: "pending_manual"
|
|
179
|
+
};
|
|
180
|
+
const nextStatus = statusByAction[action] || current.status;
|
|
181
|
+
const next = appendHistory(current, action || "review", body, nextStatus);
|
|
182
|
+
if (body && typeof body.action_code === "string" && body.action_code.trim()) {
|
|
183
|
+
next.action_code = body.action_code.trim();
|
|
184
|
+
}
|
|
185
|
+
state.refunds.set(refundId, next);
|
|
186
|
+
const remediationStatus = next.status === "completed"
|
|
187
|
+
? "compensated"
|
|
188
|
+
: next.status === "cancelled"
|
|
189
|
+
? "none"
|
|
190
|
+
: next.status === "approved"
|
|
191
|
+
? "remediation_pending"
|
|
192
|
+
: next.status === "pending_manual"
|
|
193
|
+
? "remediation_pending"
|
|
194
|
+
: next.status === "failed"
|
|
195
|
+
? "compensation_failed"
|
|
196
|
+
: "remediation_pending";
|
|
197
|
+
setResultRemediation(state, next.transaction_id, {
|
|
198
|
+
status: remediationStatus,
|
|
199
|
+
reason_code: remediationStatus === "none" ? null : next.reason_code,
|
|
200
|
+
active_case_type: remediationStatus === "compensated" || remediationStatus === "none" ? null : "refund",
|
|
201
|
+
active_case_id: remediationStatus === "compensated" || remediationStatus === "none" ? null : next.refund_id,
|
|
202
|
+
compensating_action_status: remediationStatus === "none" ? null : next.status
|
|
203
|
+
});
|
|
204
|
+
return next;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
function createRemediationTicket(state, body) {
|
|
208
|
+
const timestamp = new Date().toISOString();
|
|
209
|
+
const actor = body && body.actor && typeof body.actor === "object" ? body.actor : { type: "operator", role: "creator" };
|
|
210
|
+
const context = body && body.context && typeof body.context === "object" ? body.context : null;
|
|
211
|
+
const ticket = {
|
|
212
|
+
ticket_id: `rmt_${crypto.randomBytes(6).toString("hex")}`,
|
|
213
|
+
status: "open",
|
|
214
|
+
transaction_id: body.transaction_id || null,
|
|
215
|
+
receipt_id: body.receipt_id || null,
|
|
216
|
+
reason_code: body.reason_code || "other",
|
|
217
|
+
action_code: body.action_code || "investigate",
|
|
218
|
+
assigned_owner: typeof body.assigned_owner === "string" && body.assigned_owner.trim() ? body.assigned_owner.trim() : null,
|
|
219
|
+
note: body.note || null,
|
|
220
|
+
created_at_iso: timestamp,
|
|
221
|
+
updated_at_iso: timestamp,
|
|
222
|
+
created_actor: actor,
|
|
223
|
+
created_context: context,
|
|
224
|
+
history: [
|
|
225
|
+
{
|
|
226
|
+
at_iso: timestamp,
|
|
227
|
+
event: "opened",
|
|
228
|
+
note: body.note || null,
|
|
229
|
+
reason_code: body.reason_code || "other",
|
|
230
|
+
action_code: body.action_code || "investigate",
|
|
231
|
+
previous_status: null,
|
|
232
|
+
next_status: "open",
|
|
233
|
+
actor,
|
|
234
|
+
context
|
|
235
|
+
}
|
|
236
|
+
]
|
|
237
|
+
};
|
|
238
|
+
state.remediationTickets.set(ticket.ticket_id, ticket);
|
|
239
|
+
setResultRemediation(state, ticket.transaction_id, {
|
|
240
|
+
status: "remediation_pending",
|
|
241
|
+
reason_code: ticket.reason_code,
|
|
242
|
+
active_case_type: "remediation_ticket",
|
|
243
|
+
active_case_id: ticket.ticket_id,
|
|
244
|
+
compensating_action_status: ticket.action_code
|
|
245
|
+
});
|
|
246
|
+
return ticket;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
function actOnRemediationTicket(state, ticketId, body) {
|
|
250
|
+
const current = state.remediationTickets.get(ticketId);
|
|
251
|
+
if (!current) return null;
|
|
252
|
+
const action = String((body && body.action) || "").trim();
|
|
253
|
+
const statusByAction = {
|
|
254
|
+
acknowledge: "acknowledged",
|
|
255
|
+
assign: "assigned",
|
|
256
|
+
escalate: "escalated",
|
|
257
|
+
resolve: "resolved",
|
|
258
|
+
clear: "cleared"
|
|
259
|
+
};
|
|
260
|
+
const next = appendHistory(current, action || "acknowledge", body, statusByAction[action] || current.status);
|
|
261
|
+
if (body && body.reason_code) {
|
|
262
|
+
next.reason_code = body.reason_code;
|
|
263
|
+
}
|
|
264
|
+
if (body && body.action_code) {
|
|
265
|
+
next.action_code = body.action_code;
|
|
266
|
+
}
|
|
267
|
+
if (body && typeof body.assigned_owner === "string" && body.assigned_owner.trim()) {
|
|
268
|
+
next.assigned_owner = body.assigned_owner.trim();
|
|
269
|
+
}
|
|
270
|
+
if (body && typeof body.note === "string" && body.note.trim()) {
|
|
271
|
+
if (next.status === "assigned") {
|
|
272
|
+
next.assignment_note = body.note.trim();
|
|
273
|
+
}
|
|
274
|
+
if (next.status === "cleared") {
|
|
275
|
+
next.cleared_note = body.note.trim();
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
state.remediationTickets.set(ticketId, next);
|
|
279
|
+
const remediationStatus = next.status === "cleared"
|
|
280
|
+
? "none"
|
|
281
|
+
: next.status === "resolved"
|
|
282
|
+
? "none"
|
|
283
|
+
: next.status === "escalated"
|
|
284
|
+
? "escalated"
|
|
285
|
+
: "remediation_pending";
|
|
286
|
+
setResultRemediation(state, next.transaction_id, {
|
|
287
|
+
status: remediationStatus,
|
|
288
|
+
reason_code: remediationStatus === "none" ? null : next.reason_code,
|
|
289
|
+
active_case_type: remediationStatus === "none" ? null : "remediation_ticket",
|
|
290
|
+
active_case_id: remediationStatus === "none" ? null : next.ticket_id,
|
|
291
|
+
compensating_action_status: remediationStatus === "none" ? null : next.action_code
|
|
292
|
+
});
|
|
293
|
+
return next;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
module.exports = {
|
|
297
|
+
actOnDispute,
|
|
298
|
+
actOnRefund,
|
|
299
|
+
actOnRemediationTicket,
|
|
300
|
+
createDispute,
|
|
301
|
+
createRefund,
|
|
302
|
+
createRemediationTicket
|
|
303
|
+
};
|