JPM Integration
JP Morgan Chase is used for subscription ACH payments. JPM offers better pricing than Usio for this payment type. All JPM traffic is over the ACH network — there are no pinless or card-present flows.
Authentication
JPM requires mutual TLS (mTLS) for all API calls. Both the client (us) and the server (JPM) present certificates during the TLS handshake.
-
Certificates: Both a TLS client cert and a separate JWS signing cert are stored in AWS Secrets Manager. They are retrieved once per Lambda cold start and reused across invocations.
-
Request signing: Some JPM endpoints require the request body to be signed with a JSON Web Signature (JWS) using the signing cert.
-
Cert renewal: Both certs are created by us and submitted to JPM for renewal annually. Renewal must be coordinated before the existing certs expire — JPM does not auto-renew them.
ACH Payments
All payments submitted to JPM go over the ACH network. ACH is asynchronous — there is no immediate success or failure response. JPM initially accepts the payment (returning a confirmation ID), but the final outcome (cleared or returned) may not be known for several business days.
This means ACH payments require a reconciliation step after submission. JPM provides two mechanisms:
-
Webhooks: JPM sends a callback to our webhook endpoint when the payment status changes.
-
Status polling: Our
check-ach-workerLambda polls JPM daily to check the status of outstandingACHSENTpayments.
See Payment Syncing for how both mechanisms work together.
Payment Statuses
| Status | Description | Business Action |
|---|---|---|
|
Payment received by JPM, pending initial validation. |
No action required. |
|
Initial validation completed. Payment queued for ACH submission. |
No action required. |
|
Payment under internal review by JPM (US ACH only). |
Monitor; no action required. |
|
Payment cleared JPM internal review and is being sent to the ACH network (US ACH only). |
No action required. |
|
JPM has sent the payment to the ACH network. This is not final — the payment can still be returned by the receiving bank. |
No action required. Continue monitoring for returns. |
|
Payment failed JPM validation and was not submitted to the ACH network. |
Mark payment as |
|
Payment was submitted to the ACH network but the receiving bank returned it. Return code identifies the reason. |
Mark payment as |
COMPLETED from JPM does not mean the payment successfully debited the user’s account. It means JPM forwarded the payment to the ACH network. RETURNED is the status we receive if the bank rejects it.
|
Webhook Processing
JPM delivers status callbacks to a dedicated API Gateway endpoint at webhooks.floatme.io. The webhook handler Lambda decouples receipt from processing: it validates the payload, filters to actionable statuses, and enqueues only REJECTED and RETURNED transactions to SQS. All other statuses (COMPLETED, etc.) are acknowledged with HTTP 200 but not forwarded.
JPM │ POST /v1/jpm ▼ API Gateway (webhooks.floatme.io) │ ▼ jpm-webhook-handler (Lambda) │ validate payload │ filter: REJECTED or RETURNED only ├─ other statuses ──► HTTP 200, no SQS enqueue │ ▼ SQS (jpm-webhooks queue) │ trigger ▼ jpm-webhook-processor (Lambda) │ update payment status → FAILED │ write return_code + return_info ▼ DynamoDB (prod-payments)
The jpm-webhook-processor looks up the matching payment record by endToEndId (= confirmation_id in DynamoDB), sets payment_status = FAILED, and writes return_code and return_info from the processingInfo field. This triggers a DynamoDB Streams event that kinesis-feeder forwards to the Kinesis stream, which may trigger blocklist processing via blocklist-handler.
The JPM webhook endpoint spec is documented in spec/jpm_webhook_api.yaml. See API Specification for the Swagger UI.
Return Codes
JPM uses its own return code scheme that maps to standard NACHA codes. The blocklist system uses these mappings to determine whether a user should be blocked.
| JPM Code | NACHA Equivalent | Meaning |
|---|---|---|
|
R02 |
Account closed. |
|
R03 |
No account / unable to locate account. |
|
R04 |
Invalid account number structure. |
|
R16 |
Account blocked / frozen. |
See Blocklist for the full list of codes that trigger auto-blocklisting.
Related Pages
-
Payment Syncing — How webhook callbacks and daily ACH polling keep payment records up to date
-
Blocklist — Which return codes trigger user blocklisting and how blocks are removed
-
Prenotes — Zero-dollar ACH transactions submitted to JPM for bank account verification
-
API Specification — Full OpenAPI spec including the JPM webhook endpoint