Run the Integration Loop
The point of the MCP server is that an agent can not only write an ElasticPay integration but prove it works, end to end, with no human in the middle. The proof hinges on test clocks: instead of waiting a month to see a payment plan renew, the agent advances simulated time and watches the renewal, the payment, and the webhook events appear in seconds.
Once connected, ask your agent to run this loop. Every step is a single tool call from the tool reference. The same sandbox connection can also stage a bulk migration for the merchant to approve in the dashboard — see Migrate customers with an agent and then Migrate payment plans with an agent.
The loop
Section titled “The loop”Each step is one tool call. The payloads below are ready to paste — replace
the …_from_step_N placeholders with the ids returned by earlier steps, and
adjust the dates (start_date must be today or later, Australian Eastern
Time; the advance target must be after it).
-
Create a test clock, frozen at a known time. Note the
clk_…id in the response.create_test_clock {"frozen_time": "2026-10-01T00:00:00Z","name": "renewal-walkthrough"} -
Create a customer on the clock. Customers join a clock at creation; this is what makes their plans follow simulated time.
create_customer {"name": "Ada Lovelace","email": "ada@example.com","test_clock": "clk_from_step_1"} -
Save a payment method with a fixture token. No widget, no real card: the fixture carries a deterministic outcome.
create_payment_method {"fixture_token": "tok_sandbox_visa_succeed","customer_id": "cus_from_step_2","card_name": "Ada Lovelace"} -
Take a one-off payment: create an intent, then confirm it with the saved method. Fetch it back with
get_payment_intentand expectsucceeded.create_payment_intent {"amount": 2500,"currency": "AUD","customer_id": "cus_from_step_2","payment_method": "pm_from_step_3"}confirm_payment_intent {"payment_intent_id": "pi_from_step_4","payment_method": "pm_from_step_3"} -
Create and activate a recurring plan funded by the saved method, starting on the clock’s next simulated day.
create_payment_plan {"customer_id": "cus_from_step_2","name": "Monthly subscription","frequency_type": "monthly","recurring_amount_cents": 4900,"start_date": "2026-10-02","until_further_notice": true,"payment_instrument_id": "pm_from_step_3"}activate_payment_plan { "payment_plan_id": "pp_from_step_5" } -
Advance the clock past the start date. Advancing is asynchronous: poll
get_test_clockuntil status returns toready.advance_test_clock {"test_clock_id": "clk_from_step_1","frozen_time": "2026-10-03T00:00:00Z"}get_test_clock { "test_clock_id": "clk_from_step_1" } -
Observe the renewal — the plan has now generated a payment intent under simulated time.
list_plan_payment_intents {"payment_plan_id": "pp_from_step_5","limit": 10} -
Confirm via events — what a webhook subscriber would have received, newest first, each with delivery status. Webhooks are the source of truth — an agent that checks the event feed is verifying the same signal production code should trust.
list_webhook_events {"event_type": "payment_intent.succeeded","limit": 20}
Exercising failure
Section titled “Exercising failure”Swap the fixture in step 3 and rerun: the charge in step 4 declines instead of succeeding, which is how an agent rehearses failure handling without contriving anything.
Two things about a decline are worth knowing before you write the handling:
A decline is an outcome, not an error. confirm_payment_intent answers
normally, with the failure in the body:
{ "status": "failed", "last_payment_error": { "code": "insufficient_funds", "message": "Sandbox simulated decline: insufficient_funds" }}A declined one-off payment is not retried. Reading the intent back still
says failed, with last_payment_error kept. failed is permanent: to try
again, create a new payment intent.
A plan’s renewal also stays failed when it declines, as the renewal in
step 7 does, but the plan’s failure behaviour may retry it on a new payment
intent, when the card scheme allows one. Test
dunning
walks it.
{ "fixture_token": "tok_sandbox_visa_decline_insufficient_funds", "customer_id": "cus_from_step_2", "card_name": "Ada Lovelace"}The fixture catalogue covers declines, 3D Secure, and timeouts.
Collecting by bank debit
Section titled “Collecting by bank debit”The same loop works with a bank account. On a test clock the sandbox bank sends, settles and returns debits on the clock’s time, so an agent can watch a plan collect its total and then collect a returned debit again.
-
Save the bank fixture for the customer from step 2. It saves a sandbox bank account with an accepted direct debit mandate.
create_payment_method {"fixture_token": "tok_sandbox_au_becs_debit","customer_id": "cus_from_step_2"} -
Create and activate a plan on it, as in step 5, with a total: for example three weekly payments of
5000,number_of_payments: 3. -
Advance the clock past each payment date in turn, then a few days more. A debit is sent on its date and settles two business days later on the clock.
-
Check that the last debit has settled. The plan alone doesn’t say:
remaining_amount_centsof 0 with nonext_dateonly means nothing more will be scheduled. List the plan’s payment intents and wait for the last one to besucceeded.list_plan_payment_intents { "payment_plan_id": "pp_from_step_2", "limit": 10 } -
Return it late, as a bank would days after settlement.
simulate_bank_late_return { "payment_intent_id": "pi_last_debit", "code": "late_return" } -
Watch the plan collect it again. It stays
active, so its failure behaviour runs. UnderNEW_AT_ENDa new5000payment appears on the plan’s next payment date,remaining_amount_centsshows5000until it is sent, and advancing the clock past that date collects it.
simulate_bank_dishonour fails a debit that has been sent but not settled
in the same way. Bank debit outcomes
lists every code, and which ones stop the bank account instead.
Things agents trip on
Section titled “Things agents trip on”- Confirmation needs the payment method.
confirm_payment_intentrequirespayment_methodeven when the intent was created with one. - What you charge is not always what you refund. Where a surcharge
applies, confirm adds it on top: an intent created for
2500settles at atotal_amountof, say,2620. (Card payments on Australian accounts are never surcharged.) Refunding2500leaves the surcharge outstanding and the intentpartially_refunded. Omitamountonrefund_payment_intentto clear it in full, or refund againsttotal_amountrather than the amount you asked for. - Advancing is not instant. After
advance_test_clock, pollget_test_clockuntilreadybefore asserting anything happened. - Plan start dates are Australian.
start_datemust be today or later in Australian Eastern Time; “tomorrow” is always safe. - Clock limit. Sandbox accounts allow 3 active clocks — delete finished
ones with
delete_test_clock.