Capture Authorized Stripe Charges in NetSuite

This workflow allows you to authorize a charge outside of NetSuite and later capture the charge when a NetSuite Invoice is created. This is helpful for companies that use an eCommerce platform to collect order and use NetSuite to manage fulfillment.


This workflow is a great fit if you use NetSuite for order fulfillment, A/R collection, and customer service (refunds, returns, etc); but leverage another system (Shopify, SalesForce, etc) for order collection and payment authorization. In this workflow, your eCommerce system authorizes charges in Stripe, and SuiteSync captures the authorized charge.

For example, if you are a retailer who wants to capture a charge once the order has shipped from your warehouse, this solution is a great fit.

Here's an overview of this workflow:

  1. A charge in Stripe is authorized, but not captured
  2. A SalesOrder is created in NetSuite. The Stripe Charge ID is included on the SalesOrder.
  3. An Invoice is created from the SalesOrder. This normally happens once the order has been fulfilled.
  4. SuiteSync looks for NetSuite Invoices and captures the total amount due on the invoice.
  5. The CustomerPayment is automatically reconciled to the corresponding bank deposit. Processing fees are automatically recorded.
  6. Later on, if a CreditMemo is created from the Invoice, SuiteSync will create a refund in Stripe for the amount of the credit memo.

Here's a short video walkthrough of how this works:

Key benefits of this flow:

  • Your CX team doesn't have to use Stripe to create refunds or manage the order flow.
  • Your technical team won't have to build a custom integration or manage getting several integration systems working together.
  • Your finance team will stop managing cash application, deposit reconciliation, and fee recording. This is completely automated and happens silently in the background.

Here's a visual overview:

Authorize Capture Overview

Technical Overview

Your system is responsible for:

  1. Creating an authorized (uncaptured) Stripe charge. The payment method must be saved (by creating a Stripe customer) for the reauthorization to successfully occur in the case of an expired payment.
  2. Pushing an order to NetSuite, and adding the Stripe Charge ID to a custom field on the SalesOrder (more on this later). This is done by a NetSuite integration specific to your eCommerce application. There are pre-built integrations available for most eCommerce applications.
  3. Billing the SalesOrder by creating an Invoice

After the SalesOrder is billed, SuiteSync handles the rest of the process:

  1. SuiteSync inspects all open invoices that contain a Stripe Charge ID in the custom transaction field. When a charge is found, it is captured for the amount remaining on the invoice.
  2. If capture fails, a new charge is attempted to be created. If the new charge creation fails, a message is added to the invoice memo (more details on this process).
  3. If capture succeeds (or if a new charge is successfully created) a CustomerPayment is created and applied against the Invoice. Note that if a new payment is created, the Stripe Charge ID is updated on the NetSuite invoice before the NetSuite payment record is created.
  4. The CustomerPayment is reconciled against the corresponding bank deposit using the standard transfer reconciliation flow

What is the Recommended NetSuite Order Workflow with Auth-capture?

NetSuite's order processing workflow is very powerful and allows you to customize the workflow depending on your business logic. For instance, shipments can have multiple stages, orders can be billed before they are shipped, etc.

Below is a recommended workflow for auth-capture based on the following assumptions:

  1. Sales orders need to be fulfilled, and the fulfillment process is tracked in NetSuite
  2. Sales orders are created before payment is collected (captured)
  3. Sales order should not be fulfilled until the payment has been successfully captured

Here's the full workflow:

  1. Unapproved SalesOrders are created in NetSuite through an eCommerce integration, CSV import, etc.
  2. SalesOrders are approved by operations/fulfillment. Fraudulent orders are canceled before being approved.
  3. After a SalesOrder is approved, an Invoice for that SalesOrder is created.
  4. SuiteSync captures the charge associated with the NetSuite Invoice based on the invoice total.
  5. If capture succeeds, payment is applied against the invoice, marking it as paid.
  6. When a invoice is marked as paid, a shipment (item fulfillment) is created. The fulfillment team can watch a queue of "Pending Fulfillment" invoices create shipments or shipments can automatically be created.
  7. If capture fails, invoices remain unpaid, and the failure is indicated on the Invoice. The finance/collection team can watch the queue of unpaid invoices which failed to collect and initiate a collection process for that customer.

Note that in most cases, anything that seems "manual" can be automated using a NetSuite workflow or SuiteScript.

To support this flow, you'll need to:

  1. Enable the "Invoice in Advance of Fulfillment" NetSuite feature. This enables you to bill a SalesOrder without fulfilling it.
  2. The process for "watching" the queues described above is dependent on your business and existing processes. The simplest way to watch the queues described is a NetSuite saved search, but workflows and SuiteScripts can be used to further automate this process.

Here's instructions on how to enable these features.

Implementation Guide

Here's how to use auth-capture:

  1. Create a custom transaction body field in NetSuite called custbody_suitesync_authorization_code. We create this for you during the onboarding process (you can also create it yourself)
  2. When creating the NetSuite SalesOrder or Invoice set the custbody_suitesync_authorization_code field to the Stripe ID of the authorized charge. The Stripe ID starts with either ch_ or py_.
  3. The form used on the SalesOrder must create an Invoice in NetSuite. CashSales are not supported.
  4. If you have multiple subsidiaries, it's critical that the bank chosen connected to Stripe account is connected to the same subsidiaries as the NetSuite customers.

View Example Implementation

Can I partially capture a charge more than once?

No. Stripe only allows a charge to be captured once. Learn more about this.

Although multiple captures are not supported, if the card used for the charge was stored, SuiteSync creates a new charge for the second invoice if the original Stripe charge ID is specified on the subsequent invoices.

For instance, if you partially ship orders you'll often create multiple invoices for each partial shipment. After the first invoice is created, if each additional invoice contains the original Stripe Charge ID on the SalesOrder which was captured (which is the default behavior) then each subsequent invoice will trigger a new payment to be created in Stripe.

Can I use alternative payment methods (i.e. bank transfers) alongside an auth-capture workflow with cards?

Yes. You'll need to use CustomerDeposits for any non-card payments (only cards support auth-capture).

Are CustomerDeposits Supported?

Not in the auth-capture flow. They represent a successful pre-payment. Since charges are not successful until they are captured, SuiteSync only creates a CustomerDeposit if the payment is authorized and captured during checkout.

Note that it is possible to use auth-capture in conjunction with the standard ecommerce workflow. If you want to use alternative payment methods (i.e. non-card methods like bank transfers) you'll need to use CustomerDeposits.

Are CashSales Supported?

No. Only Invoices coupled with CustomerPayments are supported in the auth-capture workflow. This is a technical limitation: if more expanded APIs are released in the future that allow us to support CashSales, they will be supported.

To capture payment SuiteSync must:

  1. Know that the order amount is finalized/fulfilled
  2. Be able to retrieve the final total amount that should be captured. In some cases, the finalized order total may not equal the original authorization amount.

Both Invoices and CashSales contain this information.

However, CashSales represent both an Invoice and a CustomerPayment in a single record. There is no A/R accounting entry made when a CashSale is created and an entry is made directly to cash.

Stripe Charges are not captured immediately when the CashSale created. Instead, a notification is sent to SuiteSync to capture the charge when the CashSale is created. Since the capture operation is run asynchronously and may not always succeed (if the charge expired, for example), creating a CashSale would incorrectly represent the reality in your Stripe account: an entry should not be made to undeposited funds until the charge is successfully captured in Stripe.

Note that SuiteSync supports CashSales & CashRefunds if you are capturing payments immediately and not using an auth-capture workflow.

Can I capture less than the authorized amount?

Yes. The "amount due" of the invoice is captured. If the amount is less than the original authorization, then the charge is only partially captured and the rest of the authorization is released to the customer.

If the amount due is larger than the original authorization amount than a new charge is created for the higher amount due.

Are Stripe Orders (Relay) supported?

Yes. However, if you are using Stripe Orders the workflow is completely different. Learn more here.

I'd like to create an authorization for more than 7 days. What are my options?

Often customers want to authorize a card for an order that is fulfilled more than seven days in the future (pre-orders are a great example here). If you capture a charge when the order is fulfilled (in this case, after the 7 day window), the authorization would expire.

Stripe does not allow the 7 day authorization window to be extended. Your only option here is save the card and reauthorize it.

There's not a clean way to hold an authorization over a long period of time—essentially creating a new authorization over and over when the old charge expires. Each bank (and each card network!) handles authorizations slightly differently. For example, if you continually reauthorize a charge it may not look the same to the customer (they may see multiple independent authorizations on their statement for a period of time) and it might work against your end goal.

For instance, if you'd like to create four authorizations for a 30 day pre-order, the first three authorization2 may work just fine but depending on the timing on the underlying bank it's possible for the four authorization to fail in part because of the 2nd or 3rd authorization (for instance, the 3rd authorization could still be pending on the exact day the 4th authorization on the card is made).

Here's the recommended approach:

  1. Create a customer in Stripe and store the card information.
  2. Authorize a charge against the saved card
  3. When the order is ready to be shipped, charge the user's saved card.
  4. Ship the product

The key difference here is that it is important to setup your order workflow to only ship the order after the order has been paid in full.

Can I automatically release authorizations when an order is cancelled or closed?

Yes. This is an optional feature that can be enabled on your account.

Here is how it works:

  • An order can be canceled if is "Pending Approval". If the order is canceled, the full authorized amount is released back to the card.
  • An order can be closed if it is approved, but not yet partially or fully billed. Note that an order can be closed if an order is partially/fully fulfilled. If the order is closed, the authorized amount is released back to the card.

How are failures handled?

Some failures simply cause a delay (if the NetSuite or Stripe API is down, for instance) and are handled automatically. Other failures cause additional information to be added to the NetSuite invoice but are resolved automatically as well.

However, there are some errors that cannot be recovered from automatically and require intervention on your end to resolve.

What conditions can I use to detect if capture has failed for a script or workflow?

Here are the conditions you can use to detect if capture has failed, and a reauthorization on a saved card (if applicable), failed as well:

  • The "Stripe Transaction ID" field has not been modified. If a new authorization is created because of a failed capture, the "Stripe Transaction ID" field is updated at the same time as the memo on the invoice.
  • The "Memo" field contains "Stripe:". This indicates that there is a message from the integration about what happened during the capture request. This memo does not always indicate a error.
  • The Invoice is Open (i.e. unpaid)

Here are some other technical details to be aware of when developing auth-capture automation:

  • If the capture succeeds without creating a new payment, the invoice memo is not updated.

When a payment capture or reauthorization fails, you could send the user a link to a invoice payment form so they can pay their invoice. Here's some example code demonstrating how to detect if a payment has failed:

function isEmpty(obj) {
  return obj === undefined || obj === null || obj === "";
}

function contains(str, searchString) {
  return str.indexOf(searchString) != -1;
}

var SUITESYNC_TRANSACTION_ID_FIELD = "custbody_suitesync_authorization_code";

function afterSubmit(type) {
  if (type == "edit" || type == "create") {
    var oldRecord = nlapiGetOldRecord();
    var oldTransactionID = oldRecord.getFieldText(
      SUITESYNC_TRANSACTION_ID_FIELD
    );
    var currentTransactionID = nlapiGetFieldText(
      SUITESYNC_TRANSACTION_ID_FIELD
    );
    var memo = nlapiGetFieldText("memo");

    if (
      currentTransactionID == oldTransactionID &&
      !contains(memo, "Stripe:")
    ) {
      successfulCapture();
    }

    if (currentTransactionID != oldTransactionID && contains(memo, "Stripe:")) {
      successfulReauthorization();
    }

    if (currentTransactionID == oldTransactionID && contains(memo, "Stripe:")) {
      captureFailureOrReauthorizationFailure();
    }
  }
}

Authorized charge has expired

An authorized charge expires after 7 days. This authorization window cannot be changed.

If you create an Invoice in NetSuite after that 7-day period a new Stripe charge is created. The same card that was used for the original authorization is used for this new charge.

The new Stripe charge ID that is created is written to the "SuiteSync Authorization Code Field".

A note will be added to the memo indicating that a new charge was created because the original charge failed to capture. Here's what the memo will say: SuiteSync: charge ch_123 failed to capture. New charge created.

Amount due exceeds original authorized amount

First, the authorized charge is deauthorized, releasing the funds back to the customer.

Next, a new charge is authorized and captured for the amount due on the invoice. A memo is added to the invoice indicating that a new charge was created Stripe: insufficient authorized funds on ch_123. New charge created. The new charge ID is added to the "SuiteSync Authorization Code" field.

If a new charge was not successfully created, the old charge is not released. You can adjust the invoice by changing the invoice or total or applying a credit memo to the invoice in order to ensure the invoice total is less than the authorized amount. If you update the NetSuite invoice (by updating the memo or something else on the invoice) this will reprocess the invoice and cause the charge to be captured.

Partially fulfilled orders with multiple invoices

If an order is partially fulfilled, two ItemFulfillments are created, which creates two separate invoices. Stripe only allows a single capture against an authorized charge.

When the first invoice is created, the charge is captured based on the "Amount Remaining" field on the first invoice.

When the second invoice is created, a new charge must be created. This reauthorization process works identically to if the original charge authorization expired. The "SuiteSync Authorization Code" field will be overwritten with the new Stripe charge ID. A new charge is only created when the SalesOrder that the invoice is created from is the same SalesOrder that the invoice associated with the charge where the capture was initiated.

A note will be added to the memo indicating that a new charge was created.

Reauthorization failure

In order to solve many of the above failure cases, a new charge needs to be created. The new charge uses the same card as the original charge.

However, this new charge is not guaranteed to succeed. Here are examples of a couple of instances where this charge could fail:

  • The saved card information was removed from the Stripe customer
  • The card was canceled
  • The card reported insufficient funds

If these (or any other errors) occur when creating the new charge, a memo is added to the invoice indicating Stripe: payment error for customer cus_123.

Note that if the Stripe customer has additional cards that are saved, those cards are not used. Only the card used on the originally authorized charge is used.

Currency Mismatch

If the currency of the invoice is different from the currency of the Stripe charge an error message Stripe: currency mismatch is added to the memo of the invoice.

The currency of the invoice must match the currency of the payment. The integration cannot determine how much to capture if the currency does not match the invoice since that would require determining the exchange rate to use when capturing the payment.

In order to fix this issue, adjust the currency of the invoice connected to the charge.

Network or API errors

If there is a Stripe or NetSuite outage, or if there are network errors the capture operation is retried. Retries are run every hour for the next 72 hours until it succeeds. If it fails after 72 attempts, you must manually retry the operation in the SuiteSync dashboard.