Skip to content

Credit Card

A credit card payment charges the investor's card through TransactAPI's Stripe integration and moves the funds into the offering's escrow account. The investor enters the card in a hosted form, so card numbers never pass through your systems. You then call ccFundMove against the trade. Use this page when the trade's transactionType is CREDITCARD.

Before You Start

  • Your client must be enabled for credit card payments with Stripe as the provider. Contact North Capital to enable it and to confirm your per-transaction card limit.
  • Register webhooks so you receive the payment status events.
  • The investor has an approved account (accountId) that is not blocked for card transactions. See Onboard Investors.
  • A trade exists for that account with transactionType set to CREDITCARD, and its total is within your card limit. See Create the Trade. To switch an existing CREATED trade, call updateTradeTransactionType.
  • Card payments and chargebacks are billed in production. See the Fee Schedule.

Steps

Request a secure link to the card entry form for the investor's account.

curl -X POST "$TAPI_HOST/v3/linkCreditCard" \
  -H "Authorization: Bearer $TAPI_CLIENT_ID:$TAPI_API_KEY" \
  -d accountId=A12345
{
  "statusCode": "101",
  "statusDesc": "Ok",
  "accountDetails": "https://api-sandboxdash.norcapsecurities.com/admin_v3/LinkCreditCard?params=..."
}

Open the accountDetails URL for the investor, for example in a new window or an iframe. The investor enters the card there and it is saved with Stripe against the account. An account holds one card. To replace it, call updateLinkedCreditCard, which returns a form URL in the same way.

Change on November 1, 2026

Starting November 1, 2026, each URL returned by linkCreditCard and updateLinkedCreditCard is valid for 24 hours after it is generated and for one successful card save. Reopening the page within that time still works. After the card is saved, request a new URL to add or replace a card. Continue to treat the URL as opaque: the params value becomes a fixed 64-character string. URLs generated before November 1 keep working for 24 hours after the change.

Results from the hosted form

When the form is embedded in an iframe, it posts messages to your page with window.parent.postMessage. Each message is a JSON string. A form opened in a new window or tab has no parent page, so it posts nothing you can receive; check the result with step 2 instead.

When the investor submits the card, the form posts the result of saving it:

{
  "statusCode": 101,
  "statusDesc": "Ok",
  "accountDetails": "Card added successfully."
}

accountDetails reads Card updated successfully. on the updateLinkedCreditCard form. Any other statusCode means the card was not saved, and statusDesc says why:

{
  "statusCode": 710,
  "statusDesc": "Credit Card details already added for this account"
}

On the linkCreditCard form, 710 with that message means the account already has a card, for example because the investor submitted the same form twice. On either form, 710 with a different statusDesc means the card could not be saved with Stripe, and statusDesc carries Stripe's reason.

If the form fails before it saves the card, it posts an error in a different shape:

{
  "errorCode": "404",
  "errorDesc": "Error(s)",
  "error": "Your card number is incomplete."
}

This happens when the card details are rejected, when card setup cannot start, or when 3D Secure authentication fails. On the updateLinkedCreditCard form, only rejected card details are posted; a failed setup or authentication is shown to the investor but not posted.

The investor can correct the card and submit again, so an error can be followed by a success. statusCode can be a number or a string, so compare it as a string. Ignore messages whose event.origin is not your TransactAPI host (https://api-sandboxdash.norcapsecurities.com in sandbox, https://api.norcapsecurities.com in production):

window.addEventListener('message', (event) => {
  if (event.origin !== 'https://api-sandboxdash.norcapsecurities.com') return;
  const result = JSON.parse(event.data);
  if (String(result.statusCode) === '101') {
    // card saved; confirm with getLinkedCreditCard
  } else {
    // show result.statusDesc or result.error
  }
});

Not every failure produces a message, so treat getLinkedCreditCard as the record of the linked card.

2. Confirm the card is linked

Check that the investor completed the form before you charge the card.

curl -X POST "$TAPI_HOST/v3/getLinkedCreditCard" \
  -H "Authorization: Bearer $TAPI_CLIENT_ID:$TAPI_API_KEY" \
  -d accountId=A12345
{
  "statusCode": "101",
  "statusDesc": "Ok",
  "creditcardDetails": {
    "accountId": "A12345",
    "creditCardNumber": "4242",
    "cardType": "Visa",
    "createdDate": "2026-09-24 14:05:12"
  }
}

creditCardNumber is the card's last four digits, which you can show to the investor. A 715 response means no card is linked yet.

3. Create the card payment with ccFundMove

Create a card payment for the trade's full amount.

curl -X POST "$TAPI_HOST/v3/ccFundMove" \
  -H "Authorization: Bearer $TAPI_CLIENT_ID:$TAPI_API_KEY" \
  -d accountId=A12345 \
  -d tradeId=123456789 \
  -d createdIpAddress=10.0.0.1
{
  "statusCode": "101",
  "statusDesc": "Ok",
  "transactionDetails": [
    {
      "accountId": "A12345",
      "tradeId": "123456789",
      "offeringId": "12345",
      "totalAmount": "12345.000000",
      "ccreferencenumber": "987654321",
      "fundStatus": "Pending",
      "transactionstatus": "Pending"
    }
  ]
}

Keep ccreferencenumber. Status webhooks call it RefNum, and you need it to void the payment. To charge an amount other than the trade total, use ccFundMovement, which takes an additional amount. While a card payment for the trade is Pending or Submitted, another request returns 727.

4. Track the charge

The payment is approved and the card is then charged through Stripe. Approval is performed by North Capital, or happens immediately if your client is configured for real-time payments. A successful charge moves fundStatus to Submitted. A declined charge moves it to Returned, with the reason in errors. When the payment settles, the trade moves to FUNDED.

Each change sends the updateCCFundMoveStatus webhook. To check on demand, call getCCFundMoveInfo or read paymentStatus on GET /v3/trades/123456789.

If the payment is Returned, the trade stays CREATED. The investor can link a different card, and you can call ccFundMove again.

5. Void a pending payment (optional)

To cancel a card payment before it is processed, void it with its reference number. Only Pending payments can be voided.

curl -X POST "$TAPI_HOST/v3/requestForVoidCCTransaction" \
  -H "Authorization: Bearer $TAPI_CLIENT_ID:$TAPI_API_KEY" \
  -d RefNum=987654321
{
  "statusCode": "101",
  "statusDesc": "Ok",
  "investorExternalAccountDetails": "Status Updated Successfully"
}

The payment moves to Voided and the requestForVoidCCTransaction webhook fires.

Webhooks

These credit card webhooks fire in this step:

  • ccFundMove or ccFundMovement: a card payment was created.
  • stripePaymentProcess: the card was charged through Stripe.
  • updateCCFundMoveStatus: the payment's fundStatus changed.
  • updateCCFundMoveApprovedStatus: the payment's approval status changed.
  • requestForVoidCCTransaction: a pending payment was voided.

When the trade becomes FUNDED, the updateTradeStatus webhook fires.

Common Errors

See Error Codes for the full list.

Code When it happens here
710 linkCreditCard was called, or the hosted linkCreditCard form was submitted, for an account that already has a card. Use updateLinkedCreditCard. The hosted form also returns 710 when Stripe cannot save the card; see Results from the hosted form.
715 ccFundMove was called before a card was linked.
720 Your client is not enabled for credit card payments, or the trade total exceeds your card limit.
727 The trade already has a Pending or Submitted card payment.
769 Your client is not configured with Stripe as its card provider.

Next

When the trade is FUNDED, continue to Settle and Manage.