Sandbox live

Quickstart

Take a sandbox API key through to a confirmed enrolment.

Use sandbox hosts and fictional student data only. Never paste a real key, DDA JWT, or production student record.

What you will do

  1. Authenticate from your server
  2. Create a checkout (API)
  3. Redirect the payer to hosted direct-debit setup (browser)
  4. Wait until direct_debit.setup_complete is true (API)
  5. Confirm the checkout (API)
  6. Receive a completed enrolment (API)

Setup complete is not Authorised. Mandate active / Authorised can happen later and is not required for confirm. After confirm, Salesforce creates the payment schedule and owns collection. Your integration does not submit payments.

Kind Who Credential
API Your backend Authorization: Bearer <YOUR_API_KEY>
Browser Payer Setup URL token only — never your API key

1. Authenticate

StudentPay issues a sandbox key and a provider_code. Examples use SANDBOX_DEMO. Keep the key on your server.

Authorization: Bearer <YOUR_API_KEY>
Content-Type: application/json

2. Check sandbox

API — no key

GET https://sandbox-api.studentpay.co.nz/v1/environment
{
  "success": true,
  "environment": "sandbox",
  "api_base_url": "https://sandbox-api.studentpay.co.nz",
  "payment_processor": "gocardless",
  "payment_processor_status": "configured",
  "readiness": {
    "ready_for_api_calls": true
  }
}

3. Create a checkout

API — your backend

POST https://sandbox-api.studentpay.co.nz/v1/provider-checkouts
Authorization: Bearer <YOUR_API_KEY>
Content-Type: application/json
Idempotency-Key: DEMO-ENROLMENT-1001
{
  "provider": {
    "provider_code": "SANDBOX_DEMO",
    "provider_order_id": "DEMO-ENROLMENT-1001"
  },
  "student": {
    "first_name": "Alex",
    "last_name": "Student",
    "email": "alex.student@example.com"
  },
  "course": {
    "course_name": "Example Certificate"
  },
  "pricing": {
    "course_price": 2800,
    "amount_to_finance": 2790,
    "upfront_payment": 10
  },
  "plan": {
    "payment_type": "interest_free_payment_plan",
    "payment_frequency": "Weekly",
    "number_of_instalments": 186,
    "instalment_amount": 15,
    "first_payment_date": "2026-08-20"
  }
}

provider.provider_code must match the key.

Create response

{
  "success": true,
  "idempotent_replay": false,
  "provider": {
    "provider_code": "SANDBOX_DEMO",
    "provider_order_id": "DEMO-ENROLMENT-1001"
  },
  "records": {
    "contact_id": "003XXXXXXXXXXXX",
    "opportunity_id": "006XXXXXXXXXXXX",
    "dda_id": "a0XXXXXXXXXXXXX"
  },
  "checkout": {
    "checkout_id": "SANDBOX_DEMO-1720000000000-AB12CD34",
    "status": "direct_debit_setup_required",
    "requires_direct_debit": true
  },
  "direct_debit": {
    "setup_complete": false,
    "dda_id": "a0XXXXXXXXXXXXX",
    "setup_url": "https://sandbox-api.studentpay.co.nz/api/dd-setup?token=eyJ…",
    "token": "eyJ…"
  }
}

Keep checkout.checkout_id, records.opportunity_id, direct_debit.dda_id, direct_debit.setup_url, and direct_debit.token (this is checkout.checkout_token on confirm).

setup_complete is false here. That is expected.

4. Redirect the payer to hosted setup

Browser — not your API key

Open direct_debit.setup_url as a redirect or popup. The payer completes BECS NZ bank details on GoCardless. This is not a StudentPay JSON API call.

Do not send Authorization: Bearer <YOUR_API_KEY> from the browser.

5. Return, then wait for setup complete

GoCardless returns the browser to StudentPay. Treat any popup message as a hint only.

API — your backend

GET https://sandbox-api.studentpay.co.nz/v1/provider-checkouts/SANDBOX_DEMO-1720000000000-AB12CD34
Authorization: Bearer <YOUR_API_KEY>

Wait until direct_debit.setup_complete is true:

{
  "success": true,
  "checkout": {
    "checkout_id": "SANDBOX_DEMO-1720000000000-AB12CD34",
    "status": "direct_debit_setup_required"
  },
  "direct_debit": {
    "setup_complete": true,
    "billing_request_status": "fulfilled",
    "mandate_status": "pending_submission",
    "authorisation_status": "Customer In Progress",
    "authorised": false,
    "dda_id": "a0XXXXXXXXXXXXX"
  }
}

You may confirm while mandate_status is pending_submission or submitted and authorised is false.

Mandate pending_submissionsubmittedactive (Authorised) is a separate, asynchronous lifecycle. Do not wait for Authorised before confirm.

6. Confirm enrolment

API — your backend

POST https://sandbox-api.studentpay.co.nz/v1/provider-checkouts/SANDBOX_DEMO-1720000000000-AB12CD34/confirm
Authorization: Bearer <YOUR_API_KEY>
Content-Type: application/json
{
  "provider": {
    "provider_code": "SANDBOX_DEMO",
    "provider_order_id": "DEMO-ENROLMENT-1001"
  },
  "checkout": {
    "checkout_id": "SANDBOX_DEMO-1720000000000-AB12CD34",
    "checkout_token": "eyJ…",
    "opportunity_id": "006XXXXXXXXXXXX",
    "dda_id": "a0XXXXXXXXXXXXX"
  },
  "payment": {
    "payment_method": "studentpay_payment_plan",
    "first_payment_date": "2026-08-20",
    "deposit_confirmed": true
  },
  "declarations": {
    "payment_plan_accepted": true,
    "information_confirmed": true,
    "privacy_consent_accepted": true
  }
}

Always include {checkoutId} in the path. Use direct_debit.token from create. All three declarations and deposit_confirmed must be true. Confirm does not collect money.

Confirm response

{
  "success": true,
  "checkout": { "status": "confirmed", "id": "SANDBOX_DEMO-1720000000000-AB12CD34" },
  "enrolment": { "status": "complete" },
  "direct_debit": {
    "setup_complete": true,
    "mandate_status": "pending_submission"
  },
  "agreement": {
    "id": "a0JXXXXXXXXXXXX",
    "number": "PPA-000001",
    "pdf_generated": true
  },
  "already_confirmed": false
}

A repeat confirm returns already_confirmed: true and does not create a second agreement.

Done

Enrolment is complete for this API when checkout.status is confirmed and enrolment.status is complete.

What happens later, outside this API:

  • Mandate may become active / Authorised
  • Salesforce creates the payment schedule
  • Salesforce owns collection

Next