# Implementing External Payment Processing

## Overview

External Payment Processing with Peach allows you to handle all payment processing through your own payment processor while keeping Peach's system updated about payment statuses.

With this approach, you'll process payments outside of Peach's system, and then update Peach about the status of those payments through API calls. Peach is responsible for overall loan servicing management, and will apply payments to loan balances based on your payment updates via API calls, and according to your configured payment waterfall, providing borrowers with a consistent portal experience.

## Limitations and Considerations

* **CVV Storage**: Peach cannot store card CVV numbers due to PCI compliance. Some card processors can process cards without CVV, while others cannot.
* **Borrower Experience**: Borrowers will still use Peach's Borrower Portal to schedule payments, but the actual processing happens through your system.
* **Troubleshooting Complexity**: Since payment processing happens outside Peach, troubleshooting payment issues may require coordination between systems.
* **Engineering Resources**: Implementing external payment processing requires significant engineering resources compared to using Peach's direct processing options.


## Implementation Steps

### 1. Configure Your Peach Account for External Processing

First, you'll need to inform Peach that you'll be processing payments externally.

**Contact Peach to update your configuration**:

* Reach out to your Peach implementation engineer
* Request that your account be configured for external payment processing
* Confirm the change has been applied to your account


### 2. Set Up Payment Instruments

Payment instruments in Peach represent the methods borrowers use to make payments (bank accounts, debit/credit cards, etc.). For external processing, these instruments must be marked as external.

### Create a New External Payment Instrument


```
POST /api/people/{personId}/payment-instruments
Content-Type: application/json
X-API-KEY: <YOUR-API-KEY>

{
   "accountHolderName": "John Doe",
   "accountHolderType": "personal",
   "accountNumber": "4995672067",
   "accountType": "savings",
   "instrumentType": "bankAccount",
   "routingNumber": "123456789",
   "createdAt": "2023-01-01T00:00:00Z",
   "updatedAt": "2023-01-01T00:00:00Z",
   "deletedAt": null,
   "isExternal": true,
   "pendingAt": null,
   "externalId": "ext-123",
   "status": "pending",
   "inactiveReason": "updated",
   "verified": false,
   "nickname": "My Savings",
   "institutionName": "Example Bank",
   "sendNotice": true
}
```

**Response**

The following response shows a successful creation of an external payment instrument (status code 201). The created bank account has ID "PT-MJ1O-WMRJ", is marked as active, verified, and configured for external processing (isExternal:true).


```
{
    "status": 201,
    "data": [
        {
            "createdAt": "2025-03-13T03:17:43.416082+00:00",
            "updatedAt": "2025-03-13T03:17:43.429024+00:00",
            "deletedAt": null,
            "id": "PT-MJ1O-WMRJ",
            "object": "bankAccount",
            "externalId": null,
            "companyId": "CP-L9BN-5J52",
            "instrumentType": "bankAccount",
            "status": "active",
            "inactiveReason": null,
            "activeAt": "2025-03-13T03:17:42.934412+00:00",
            "inactiveAt": null,
            "pendingAt": null,
            "nickname": "Personal Bank Account",
            "verified": true,
            "isExternal": true,
            "failureReason": null,
            "failureDescriptionShort": null,
            "failureDescriptionLong": null,
            "accountNumber": "cv94slu8dsjs73900l20",
            "accountNumberLastFour": "2323",
            "routingNumber": "021313103",
            "accountType": "checking",
            "accountHolderType": "personal",
            "accountHolderName": "Jane Borrower",
            "institutionName": "CITIZENS BANK NA",
            "accountLink": null
        }
    ],
    "message": "Created payment instrument"
}
```

### Convert Existing Payment Instruments to External

For existing payment instruments, update them to mark them as external:


```
PUT https://api.peachfinance.com/api/people/{personId}/payment-instruments/{instrumentId}
Content-Type: application/json
X-API-KEY: <YOUR-API-KEY>

{
  "isExternal": true
}
```

**Note**: Peach can help bulk update existing payment instruments to isExternal=true upon request.

### Handling Borrower-Added Payment Methods

When borrowers add payment methods through the Borrower Portal, they'll be created with:

* isExternal=true
* status=pending (for manually entered methods)
* status=active (for bank accounts added via Plaid)


For manually added payment instruments, you'll need to verify and activate them:


```
PUT https://api.peachfinance.com/api/people/{personId}/payment-instruments/{instrumentId}
Content-Type: application/json
X-API-KEY: <YOUR-API-KEY>

{
  "status": "active",
  "verified": true
}
```

### 3. Set Up Webhook Consumption

To know when to process payments, you'll need to listen for events from Peach, such as:

* payment.scheduled - When a payment is first scheduled
* payment.due.today - When a scheduled payment is due


### Configure Webhook Endpoints


```
POST https://api.peachfinance.com/api/webhooks
Content-Type: application/json
X-API-KEY: <YOUR-API-KEY>

{
  "webhookType": "direct",
  "eventTypes": [
    "payment.scheduled",
    "payment.due.today"
  ],
  "enabled": true,
  "hydrateData": false,
  "webhookDirectDetails": {
    "webhookUrl": "https://webhook.site/bbdfd6e1-ce89-4995-8e73-379ebd0464b1"
  }
}
```

Your webhook endpoint should:

1. Validate the webhook signature
2. Parse the webhook payload
3. Process the event based on its type


### 4. Handle Payment Processing

When you receive a payment.due.today event, you'll need to process the payment with your payment processor.

### Extract Payment Details from Event

From the webhook payload, extract:

* Loan ID
* Payment amount
* Payment instrument details
* Due date


### Process Payment with Your Payment Processor

Use your payment processor's API to process the payment:

1. Submit the payment to your processor
2. Capture the processor's reference ID
3. Store the mapping between Peach's transaction ID and your processor's reference ID


### 5. Update Transaction Status in Peach

As the payment moves through its lifecycle with your processor, update its status in Peach.

**Note**: External transactions created with status initiated, pending, or succeeded are immediately applied to the loan balance.

### When Payment Is Initiated


```
PUT https://api.peachfinance.com/api/people/{personId}/loans/{loanId}/transactions/{transactionId}
Content-Type: application/json
X-API-KEY: <YOUR-API-KEY>

{
  "amount": 150.00,
  "paymentInstrumentId": "PT-MJ1O-WMRJ",
  "type": "oneTime",
  "status": "initiated"
}
```

### When Payment Is Pending


```
PUT https://api.peachfinance.com/api/people/{personId}/loans/{loanId}/transactions/{transactionId}
Content-Type: application/json
X-API-KEY: <YOUR-API-KEY>

{
  "amount": 150.00,
  "paymentInstrumentId": "PT-MJ1O-WMRJ",
  "type": "oneTime",
  "status": "pending"
}
```

### When Payment Succeeds


```
PUT https://api.peachfinance.com/api/people/{personId}/loans/{loanId}/transactions/{transactionId}
Content-Type: application/json
X-API-KEY: <YOUR-API-KEY>

{
  "amount": 150.00,
  "paymentInstrumentId": "PT-MJ1O-WMRJ",
  "type": "oneTime",
  "status": "succeeded"
}
```

### When Payment Fails


```
PUT https://api.peachfinance.com/api/people/{personId}/loans/{loanId}/transactions/{transactionId}
Content-Type: application/json
X-API-KEY: <YOUR-API-KEY>

{
  "amount": 150.00,
  "paymentInstrumentId": "PT-MJ1O-WMRJ",
  "type": "oneTime",
  "status": "failed"
}
```

**Important**: When a payment fails, Peach will automatically trigger a replay to adjust the loan balance, removing the payment's impact on balances, and recalculating interest as if the payment never happened.

The response will include an associated transactionId (TX-1A1A-2B2B) for each status change.

### 6. Handling Autopay

For borrowers enrolled in Autopay, Peach will generate scheduled transactions according to the Autopay schedule. When these transactions are due, Peach will send a payment.due.today event:

1. Receive the payment.due.today event for an Autopay payment
2. Process the payment with your payment processor
3. Update the transaction status in Peach as the payment progresses


### 7. Creating External Transactions

You can also create new transactions to record payments that were processed outside Peach:


```
POST https://api.peachfinance.com/api/people/{personId}/loans/{loanId}/transactions
Content-Type: application/json
X-API-KEY: <YOUR-API-KEY>

{
  "amount": 100.0,
  "type": "oneTime",
  "isExternal": true,
  "externalId": "your-external-transaction-id",
  "paymentInstrumentId": "payment-instrument-id",
  "status": "succeeded"
}
```

## Transaction Status Flow

maybe: When a borrower makes a payment, the transaction may pass through one or more of the below statuses as it's processed:

1. scheduled - Payment is scheduled for a future date; not yet applied to the loan balance
2. initiated - Payment has been initiated with the processor; applied to the loan balance
3. pending - Payment is being processed; applied to the loan balance
4. succeeded - Payment was successfully completed; remains applied to the loan balance
5. failed - Payment failed; removed from the loan balance with a loan replay
6. inDispute - Payment is in dispute; removed from the loan balance with a loan replay
7. canceled - Payment was canceled before processing (only available for scheduled status)
8. chargeback - Payment was disputed and ruled against the lender; removed from the loan balance


## Error Handling

### Common Errors

* **Payment Instrument Not Found**: Ensure the payment instrument exists and is marked as external
* **Invalid Transaction Status Transition**: Check that you're following the allowed status transition flows
* **Missing Required Fields**: Verify all required fields are included in your requests
* **Authentication Failures**: Confirm your API key is valid and has the necessary permissions


## Galileo Integration (If Applicable)

If your Peach account is configured with Galileo for third-party notification:

1. Make createPayment() API calls to Galileo when receiving payment.created or payment.initiated webhooks
2. Make createAdjustment() API calls to Galileo when receiving payment.reversed webhooks
3. Make these calls regardless of payment instrument type or isExternal status


## Next steps

- **[BYOB Processing](/payments/byob-processing)** — An alternative where Peach generates NACHA files but your bank settles.
- **[Payment Processing Overview](/payments)** — Index of payment processing options and concepts.