How to Sync Stripe Payments to NetSuite Automatically

Automatically create NetSuite payment records from Stripe transactions.

What This Integration Does

Every Stripe payment eventually needs a matching customer payment record in NetSuite, or your AR ages incorrectly and finance can't close the month. Manually reconciling Stripe payouts to NetSuite invoices is one of the more painful month-end tasks. This workflow does it continuously: each successful payment intent becomes a NetSuite customer payment within minutes.

The workflow runs on a Stripe webhook (or a scheduled poll), pulls the full payment intent, finds the corresponding NetSuite customer and open invoice, and creates a customer payment that closes the invoice. The result is a clean trail from card swipe to GL posting with no spreadsheet in between.

Prerequisites

  • A Stripe connection with read access to payment intents and charges.
  • A NetSuite connection with permission to create customer payments and read invoices and customers.
  • A convention for tying Stripe payments to NetSuite invoices (a metadata field on the Stripe payment intent containing the NetSuite invoice ID is the cleanest approach).

Step 1: Trigger on Stripe Payment Success

Add a Trigger node set to Webhook and register it in Stripe for the payment_intent.succeeded event. Spojit validates the signature. As a fallback, you can also run a Schedule trigger every few minutes that calls stripe list-payment-intents filtered by status=succeeded and a created window to catch any missed webhooks.

Step 2: Fetch the Full Payment Intent

Add a Connector node pointing at stripe using list-payment-intents filtered to the intent ID from the trigger (or just read the trigger payload if it includes the full object). You need the amount, currency, customer, and metadata.

Step 3: Resolve the NetSuite Customer

Map the Stripe customer to a NetSuite customer. The cleanest approach is to store the NetSuite internal ID in Stripe customer metadata at creation time. If you don't have that, add a Connector node pointing at netsuite using run-suiteql:

SELECT id FROM customer WHERE email = '{{ stripeCustomer.email }}' AND isinactive = 'F'

Branch on no-match with a Condition and route those payments to a Slack channel for manual handling rather than creating an orphaned record.

Step 4: Find the Open Invoice

If the Stripe payment intent metadata carries a NetSuite invoice ID, use it directly. Otherwise call netsuite list-invoices for the customer, filter to open invoices, and pick the one whose amount matches the payment. Confirm currency match. If multiple candidates exist, pause the workflow with a Human approval step rather than guessing.

Step 5: Create the Customer Payment

Add a Connector node pointing at netsuite using create-record with type customerpayment. Map:

  • customer: NetSuite internal ID resolved in Step 3.
  • payment: amount in account currency (convert Stripe cents with math round).
  • account: the bank or clearing account that mirrors Stripe in NetSuite.
  • apply: a sublist entry that applies the full payment to the invoice from Step 4.
  • externalid: the Stripe payment intent ID, for idempotency and audit.

Setting externalid is what lets you safely retry: if NetSuite already has a record with this external ID, the create returns the existing record instead of duplicating.

Step 6: Confirm and Reconcile

Use netsuite get-invoice after the payment to confirm the invoice is now closed (or partially paid). Post a slack send-message to your finance channel with the payment amount, the closed invoice number, and the link to the NetSuite record. On failure, route the same channel with the error and the Stripe payment intent ID so finance can intervene.

Tips

  • Always pass the NetSuite invoice ID in Stripe payment intent metadata at checkout. It eliminates fuzzy matching in Step 4 entirely.
  • If you have multiple Stripe accounts (different brands or regions), tag each event with the account ID and map to a different NetSuite subsidiary in Step 5.
  • Send the payment in the invoice's currency. Trying to apply a USD payment to an AUD invoice will fail or create FX noise.

Common Pitfalls

  • Stripe fees. The payment amount in NetSuite should be the gross, with the fee booked separately as a journal entry against your processing-fees account.
  • Partial payments. If a Stripe payment intent settles less than the invoice total (e.g., a manual partial capture), the customer payment must apply only that amount and leave the invoice open.
  • Webhook duplicates. Stripe retries on non-2xx. The externalid on the NetSuite record is the only thing that prevents duplicate customer payments.

Testing

Use Stripe test mode against a sandbox NetSuite. Create a test invoice with the NetSuite ID embedded in Stripe metadata, simulate a successful payment intent, and verify the customer payment appears in NetSuite, the invoice closes, and a Slack message lands. Repeat with a partial payment and a no-match customer before enabling the production webhook.

Learn More

Did this answer your question? Thanks for the feedback There was a problem submitting your feedback. Please try again later.