---
id: TST-ACC-TRX-082
title: Tender suite
kind: integration
product_area: accounting
module: transactions
revision: 1
status: draft
source_sha:
  services-accounting: f47cb263875bbc0c5640fba7dae6785ec32695f8
repository: services-accounting
files:
  - >-
    modules/transactions/tests/integration/services/payments/tender.integration.spec.js
implements:
  - ATS-ACC-TRX-007-01
  - ATS-ACC-TRX-007-02
  - ATS-ACC-TRX-007-03
  - ATS-ACC-TRX-007-04
  - ATS-ACC-TRX-007-05
  - ATS-ACC-TRX-007-08
  - ATS-ACC-TRX-007-09
implements_parts:
  - scenario: ATS-ACC-TRX-007-01
    assertion: >-
      it refuses an inactive merchant account (merchant_account_inactive),
      charging nothing
    provenance: test
    source: >-
      services-accounting:modules/transactions/tests/integration/services/payments/tender.integration.spec.js#it
      refuses an inactive merchant account (merchant_account_inactive), charging
      nothing
  - scenario: ATS-ACC-TRX-007-01
    assertion: it refuses a merchant account of another unit (cash_account_mismatch)
    provenance: test
    source: >-
      services-accounting:modules/transactions/tests/integration/services/payments/tender.integration.spec.js#it
      refuses a merchant account of another unit (cash_account_mismatch)
  - scenario: ATS-ACC-TRX-007-01
    assertion: it refuses a card payment with no merchant account (cash_account_mismatch)
    provenance: test
    source: >-
      services-accounting:modules/transactions/tests/integration/services/payments/tender.integration.spec.js#it
      refuses a card payment with no merchant account (cash_account_mismatch)
  - scenario: ATS-ACC-TRX-007-02
    assertion: >-
      it refuses a planned allocation over the line’s balance
      (over_application), charging nothing
    provenance: test
    source: >-
      services-accounting:modules/transactions/tests/integration/services/payments/tender.integration.spec.js#it
      refuses a planned allocation over the line’s balance (over_application),
      charging nothing
  - scenario: ATS-ACC-TRX-007-02
    assertion: >-
      it resolves the intent succeeded, naming the payment, claimed and
      resolved, with the batch chosen at initiation
    provenance: test
    source: >-
      services-accounting:modules/transactions/tests/integration/services/payments/tender.integration.spec.js#it
      resolves the intent succeeded, naming the payment, claimed and resolved,
      with the batch chosen at initiation
  - scenario: ATS-ACC-TRX-007-02
    assertion: >-
      it replays the same key and body as the same payment, charging nothing
      more
    provenance: test
    source: >-
      services-accounting:modules/transactions/tests/integration/services/payments/tender.integration.spec.js#it
      replays the same key and body as the same payment, charging nothing more
  - scenario: ATS-ACC-TRX-007-03
    assertion: >-
      it refuses processor_declined and fails the intent with the processor’s
      code, writing no payment
    provenance: test
    source: >-
      services-accounting:modules/transactions/tests/integration/services/payments/tender.integration.spec.js#it
      refuses processor_declined and fails the intent with the processor’s code,
      writing no payment
  - scenario: ATS-ACC-TRX-007-03
    assertion: >-
      it frees the key: the same key sent again is tendered again, as a new
      intent (declined again here)
    provenance: test
    source: >-
      services-accounting:modules/transactions/tests/integration/services/payments/tender.integration.spec.js#it
      frees the key: the same key sent again is tendered again, as a new intent
      (declined again here)
  - scenario: ATS-ACC-TRX-007-03
    assertion: >-
      it takes the same key with another card: the payment is made, the failed
      intents kept as history
    provenance: test
    source: >-
      services-accounting:modules/transactions/tests/integration/services/payments/tender.integration.spec.js#it
      takes the same key with another card: the payment is made, the failed
      intents kept as history
  - scenario: ATS-ACC-TRX-007-03
    assertion: 'it refuses processor_unavailable and fails the intent: nothing was charged'
    provenance: test
    source: >-
      services-accounting:modules/transactions/tests/integration/services/payments/tender.integration.spec.js#it
      refuses processor_unavailable and fails the intent: nothing was charged
  - scenario: ATS-ACC-TRX-007-04
    assertion: >-
      it is capped at what the line owes then and the rest stays unapplied: the
      charged money is never refused
    provenance: test
    source: >-
      services-accounting:modules/transactions/tests/integration/services/payments/tender.integration.spec.js#it
      is capped at what the line owes then and the rest stays unapplied: the
      charged money is never refused
  - scenario: ATS-ACC-TRX-007-05
    assertion: >-
      it refuses tender_uncertain and leaves the intent processing, the charge
      made once
    provenance: test
    source: >-
      services-accounting:modules/transactions/tests/integration/services/payments/tender.integration.spec.js#it
      refuses tender_uncertain and leaves the intent processing, the charge made
      once
  - scenario: ATS-ACC-TRX-007-05
    assertion: >-
      it answers the payment to a replay of the same key, from what the
      processor holds, with no second charge
    provenance: test
    source: >-
      services-accounting:modules/transactions/tests/integration/services/payments/tender.integration.spec.js#it
      answers the payment to a replay of the same key, from what the processor
      holds, with no second charge
  - scenario: ATS-ACC-TRX-007-08
    assertion: 'it is abandoned by the sweeper after 24 hours, with its event (FC-17)'
    provenance: test
    source: >-
      services-accounting:modules/transactions/tests/integration/services/payments/tender.integration.spec.js#it
      is abandoned by the sweeper after 24 hours, with its event (FC-17)
  - scenario: ATS-ACC-TRX-007-09
    assertion: >-
      it records the capture as one eligible settlement item keyed
      payment:<id>:capture, on Stripe’s timing (the account names no processor)
    provenance: test
    source: >-
      services-accounting:modules/transactions/tests/integration/services/payments/tender.integration.spec.js#it
      records the capture as one eligible settlement item keyed
      payment:<id>:capture, on Stripe’s timing (the account names no processor)
environment: local
implementation_state: implemented
latest_result: not-run
latest_run_at: null
latest_run_ref: null
conformance: not-verified
estimated_minutes: 3
isolation: >-
  Suite-marked records in the prepared local test database; fixture teardown
  removes owned records.
provenance: test
source: >-
  services-accounting:modules/transactions/tests/integration/services/payments/tender.integration.spec.js
evidence:
  - provenance: test
    source: >-
      services-accounting:modules/transactions/tests/integration/services/payments/tender.integration.spec.js
    assertion_titles:
      - receivePayment through the processor (integration)
      - >-
        a visa capture of 7000 allocated to the first line and the rest to the
        invoice
      - >-
        answers a succeeded card payment in the merchant account with the
        processor’s reference and the card’s marks
      - >-
        resolves the intent succeeded, naming the payment, claimed and resolved,
        with the batch chosen at initiation
      - >-
        allocates as planned: 6000 on the first line, the remaining 1000 on the
        second by pay priority
      - >-
        records the capture as one eligible settlement item keyed
        payment:<id>:capture, on Stripe’s timing (the account names no
        processor)
      - >-
        writes the facts of the payment item and the lines, summing to the
        unapplied money and the balances
      - >-
        records the intent’s and the payment’s document events and the tender’s
        payment events
      - >-
        refuses another person’s use of the key (idempotency_key_in_use, 409),
        charging nothing more
      - 'replays the same key and body as the same payment, charging nothing more'
      - an electronic check
      - is recorded pending_settlement with its capture item not yet eligible
      - a decline
      - >-
        refuses processor_declined and fails the intent with the processor’s
        code, writing no payment
      - >-
        frees the key: the same key sent again is tendered again, as a new
        intent (declined again here)
      - >-
        takes the same key with another card: the payment is made, the failed
        intents kept as history
      - an unreachable processor
      - 'refuses processor_unavailable and fails the intent: nothing was charged'
      - 'a lost answer, finished by a replay'
      - >-
        refuses tender_uncertain and leaves the intent processing, the charge
        made once
      - >-
        answers the payment to a replay of the same key, from what the processor
        holds, with no second charge
      - 'a lost answer, finished by the sweeper (the capture-death case, FC-23)'
      - leaves an intent younger than the first lookup step (30 seconds) alone
      - >-
        resolves the intent once a lookup is due from what the processor holds:
        the payment recorded, one charge
      - an intent the processor does not know by its key (INTG-M2 M2-D2)
      - >-
        stays processing past the first steps, however often the key lookup
        answers not_found, until a person is asked
      - an intent the processor cannot be asked about for a day
      - 'is abandoned by the sweeper after 24 hours, with its event (FC-17)'
      - a planned allocation the line can no longer take at the outcome
      - >-
        is capped at what the line owes then and the rest stays unapplied: the
        charged money is never refused
      - a visa tender that names no batch (journey 0510 step 7)
      - >-
        lands the visa tender in the batch of the visa-card rule, not the
        default
      - 'lands a mastercard tender, which the rule leaves out, in the default'
      - the processor a merchant account names (DEF-ACT-0334)
      - >-
        reads the merchant account’s processor, payrix, on the payment and on
        its intent, not the adapter’s id
      - >-
        falls back to the adapter in force, dummy, when the merchant account
        names no processor
      - its refusals before the charge
      - >-
        refuses an inactive merchant account (merchant_account_inactive),
        charging nothing
      - refuses a merchant account of another unit (cash_account_mismatch)
      - refuses a card payment with no merchant account (cash_account_mismatch)
      - >-
        refuses a planned allocation over the line’s balance (over_application),
        charging nothing
    imports:
      - 'const { sql } = require(''drizzle-orm'');'
      - >-
        const { manualClock } =
        require('@rhythmsoftware/macroservice-time/clock');
      - 'const { stage } = require(''@rhythmsoftware/macroservice-testing/stage'');'
      - >-
        const { TEST_NOW } =
        require('@rhythmsoftware/macroservice-testing/execution-context');
      - >-
        const { dummyPaymentProcessor } =
        require('../../../../services/payments/dummy-processor');
      - >-
        const { sweepProcessingIntents } =
        require('../../../../services/payments/intent-sweeper');
      - >-
        const { receivePayment } =
        require('../../../../services/payments/payment-receive');
      - >-
        const { saveBatchAssignmentSettings } =
        require('../../../../../batches/services/persistence/batch-assignment-settings');
      - >-
        const { createRecurringBatchWithFirstBatch } =
        require('../../../../../batches/services/roll/first-batch');
      - >-
        const { usePaymentProcessorForTests } =
        require('../../../../services/payments/ports/payment-processor');
      - >-
        const { createMerchantAccount } =
        require('../../../../../books/services/persistence/merchant-accounts');
      - '} = require(''../../payments-fixtures'');'
      - "\t\t\tconst { invoice } = world.require();"
      - "\t\t\tconst { payment, intent } = captured.require();"
      - "\t\t\tconst { payment, intent } = captured.require();"
      - "\t\t\tconst { invoice, payment } = captured.require();"
      - "\t\t\tconst { payment, intent } = captured.require();"
      - "\t\t\tconst { invoice, payment } = captured.require();"
      - "\t\t\tconst { payment, intent } = captured.require();"
      - "\t\t\tconst { intent } = captured.require();"
      - "\t\t\tconst { invoice, payment, intent } = captured.require();"
      - "\t\t\tconst { refusal, intent } = lost.require();"
      - "\t\t\tconst { intent } = lost.require();"
      - "\t\t\tconst { intent, tooEarly } = lost.require();"
      - "\t\t\tconst { intent, swept } = lost.require();"
      - "\t\t\tconst { cards, visa } = routed.require();"
      - "\t\t\tconst { daily, mastercard } = routed.require();"
      - "\t\t\tconst { payrix, payrixIntent } = named.require();"
      - "\t\t\tconst { unnamed } = named.require();"
    test_double_declarations: []
---

# Tender suite

## Verification Boundary

The suite exercises transaction commands and database protections in the local prepared test environment. Registration records the existing executable file; it does not report a run or establish product approval.

## What This Suite Proves

Its cited assertions contribute evidence for invalid merchant destinations cannot charge; eligibility precedes repeat-safe capture; definite failures permit another attempt; concurrent settlement cannot reject captured value; uncertain capture is recovered by lookup; unqueryable old intent becomes abandoned; bank clearing finishes pending receipts. The structured assertion bindings identify which parts of combined scenarios it executes.

## What It Does Not Prove

Local commands and route invocations do not establish deployed routing, live processor guarantees or the complete staff browser experience. An unbound feature chain remains proposed, and this registration carries no execution result.

## Real Dependencies and Test Doubles

The suite uses the imported commands, route handlers or persistence code against the local test database. Fixture helpers supply accounting context; any stepped clock, dummy processor or substituted external client is retained in the evidence declarations.

## Must Not Be Doubled

The command or persistence behavior under assertion and its relevant database protections must remain real. Substituting the final financial result removes evidence of transactional effects.
