What you will build.
Follow DEMO-ORDER-01 through checkout, confirmed payment and fulfillment. A customer returns to the success page, but the authoritative payment may still be pending. Your local exercise must not release the order merely because a browser redirected successfully.
Your working record
Create a private worksheet with columns for the source reference, observed state, responsible system, observation time, expected result and unresolved issue. Use the same fictional case throughout the steps; do not paste live personal or financial records into this exercise.
Run the example before changing it.
Inspect the baseline record below. Download and read the complete JavaScript file before running it. It uses only Node’s built-in test and assertion modules; there is no package installation, account connection or secret to configure.
{
"notificationVerified": true,
"authoritativeState": "paid",
"fulfilled": false
}Save it as crypto-payments-gateway.mjs. Open a terminal in that folder and run:
node --version
node --test crypto-payments-gateway.mjsExpected result: four passing tests and zero failures. These tests verify the teaching classifier only. They do not verify a production integration, security boundary or financial outcome.
File or test runner will not start?
Check that Node.js 22 or later is installed, the filename ends in .mjs rather than .mjs.txt, and your terminal is in the download folder. If a managed browser blocks scripts, open “Read the full exercise” and save that text locally under the same filename. Do not disable organizational security controls.
Read the full exercise
// Hybrid-Chain offline teaching model: crypto-payments-gateway
// Fictional fields/states; NOT an API client, authorization system or production implementation.
// No network calls, credentials, payments or file writes. Review before running.
// Requires Node.js 22+. Run: node --test crypto-payments-gateway.mjs
import test from 'node:test';
import assert from 'node:assert/strict';
export function inspect(record) {
if (!record.notificationVerified) return 'REJECT_NOTIFICATION';
if (record.fulfilled) return 'ALREADY_FULFILLED';
if (record.authoritativeState !== 'paid') return 'WAIT_FOR_PAYMENT';
return 'READY_FOR_FULFILLMENT_REVIEW';
}
const baseline = {
"notificationVerified": true,
"authoritativeState": "paid",
"fulfilled": false
};
const cases = [
{
"name": "payment ready for delivery review",
"patch": {},
"expected": "READY_FOR_FULFILLMENT_REVIEW"
},
{
"name": "invalid callback",
"patch": {
"notificationVerified": false
},
"expected": "REJECT_NOTIFICATION"
},
{
"name": "pending payment",
"patch": {
"authoritativeState": "pending"
},
"expected": "WAIT_FOR_PAYMENT"
},
{
"name": "repeated paid notification",
"patch": {
"fulfilled": true
},
"expected": "ALREADY_FULFILLED"
}
];
for (const example of cases) {
test(example.name, () => {
assert.equal(inspect({...baseline, ...example.patch}), example.expected);
});
}
Know what the tests demonstrate
- payment ready for delivery review
READY_FOR_FULFILLMENT_REVIEW- invalid callback
REJECT_NOTIFICATION- pending payment
WAIT_FOR_PAYMENT- repeated paid notification
ALREADY_FULFILLED
Now change one fixture at a time. If you change a case’s input without changing its expected result, the test should fail. Read the actual-versus-expected difference and use the walkthrough to decide whether your rule or your expectation needs correcting. Keep an unchanged copy for comparison.
Work through the case, step by step.
- 01
Map the three records
Do this
- Create DEMO-ORDER-01 in a worksheet and add empty columns for payment reference and fulfillment reference.
- Record the expected currency and amount before creating a checkout through any later authorized integration.
- Write down the source of truth for payment state and the system responsible for delivery.
Expected resultOrder creation, provider payment and delivery remain separate records that can be reconciled after a failure.
If your result is different
If a return URL is your only evidence of payment, stop and identify a supported authoritative status route.
- 02
Validate before acting on a notification
Do this
- Run the offline baseline with notificationVerified=true and authoritativeState=paid.
- Set notificationVerified=false and confirm REJECT_NOTIFICATION.
- Treat this flag as a placeholder for real provider authentication, replay handling and schema checks—not a signature verifier.
Expected resultThe design has a clear boundary between receiving a message and trusting it enough to inspect payment status.
If your result is different
Never let a browser parameter or an unchecked JSON field set notificationVerified in the actual implementation.
- 03
Keep pending orders undelivered
Do this
- Change authoritativeState to pending and expect WAIT_FOR_PAYMENT.
- Repeat after an imagined browser success redirect; the result must still be pending.
- Record the support path for an abandoned checkout and for a paid order whose delivery service is unavailable.
Expected resultA customer-facing page can explain pending or paid-but-undelivered without charging again or fabricating completion.
If your result is different
When status reads fail, show unresolved payment status and retry safely; do not convert a transport error into paid or declined.
- 04
Exercise duplicate fulfillment
Do this
- Set fulfilled=true and confirm ALREADY_FULFILLED, even for a verified paid notification.
- Identify the durable unique order/delivery key that the real fulfillment service will enforce.
- Save the outcomes and test concurrent callbacks separately when the actual merchant integration is available.
Expected resultThe local model explains the duplicate boundary; the integration backlog includes the atomic implementation and concurrency test.
If your result is different
A boolean in this script does not provide production exactly-once delivery. Use a durable transactional design in the owning service.
Review what you produced.
Save the worksheet, unmodified test output, one deliberately failing case and the explanation of its fix. A useful result includes an unresolved item with an owner; it is not just a screenshot of four green tests.
Map the model to actual systems.
Your commerce application owns the order, callback validation, retry-safe fulfillment, customer support, and refund policy. A redirect is not proof of payment.
For each worksheet field, name the owning system, exact current operation, permission and error behavior. Put “not available” next to any missing operation and assign a dependency owner. Do not substitute the teaching classifier for that missing integration.
Open the current API reference and inspect each operation’s request, response, scope and implementation status. Code examples on this page are local models only; they do not call those operations.
Take the next technical step.
Use your worksheet and failing-case notes in the next walkthrough. Keep the same reference trail so you can see which assumptions become actual observations—and which remain dependencies.
Inspect funding and settlement states