← Back to Guides

Stripe Integration

Combine Kynode business verification with Stripe Billing or Payments to reduce fraud, support KYB compliance, and only onboard customers you trust.

Why verify before Stripe

Korean B2B signups often need a valid business registration number. Verifying with Kynode before creating a Stripe Customer ensures metadata matches official records, lowers chargeback and account abuse risk, and gives your risk team an audit trail tied to Stripe objects.

Recommended flow

User submits company + business number
        │
        ▼
┌───────────────────┐
│ Kynode verify API │
└─────────┬─────────┘
          │ valid?
          ▼
┌───────────────────┐     metadata: business_number,
│ Stripe Customer   │     kynode_verified_at, status
└─────────┬─────────┘
          ▼
┌───────────────────┐
│ Checkout / Payment│
└───────────────────┘
  1. User submits company details and business number in your app.
  2. Your backend calls Kynode `/v1/verify/business` and checks `valid`.
  3. If valid, create a Stripe Customer (or update an existing one) with verification metadata.
  4. Proceed to Checkout, SetupIntent, or Invoice flows using that customer.

End-to-end example

Both snippets assume environment variables `KYNODE_API_KEY` and `STRIPE_SECRET_KEY`, and a normalized `business_number`. Adjust error mapping for your API.

JavaScript (Node + Stripe SDK)

import Stripe from 'stripe';

const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);
const KYNODE_KEY = process.env.KYNODE_API_KEY!;

export async function verifyAndCreateCustomer(
  businessNumber: string,
  startDate: string,
  ownerName: string,
  email: string
) {
  const verifyRes = await fetch('https://kynode-api.kynode.workers.dev/v1/verify', {
    method: 'POST',
    headers: {
      Authorization: `Bearer ${KYNODE_KEY}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      business_number: businessNumber.replace(/-/g, ''),
      startDate,
      ownerName,
      company_name: '노드메트릭스',
      language: 'en',
    }),
  });

  const verifyBody = await verifyRes.json().catch(() => ({}));
  if (!verifyRes.ok) {
    throw new Error(verifyBody.message || `Kynode error ${verifyRes.status}`);
  }

  if (!verifyBody.isValid) {
    const err = new Error('Business could not be verified');
    (err as Error & { kynode?: unknown }).kynode = verifyBody;
    throw err;
  }

  const info = verifyBody.businessInfo;
  const customer = await stripe.customers.create({
    email,
    name: info.companyName,
    metadata: {
      business_number: info.businessNumber,
      kynode_verified_at: new Date().toISOString(),
      kynode_status: String(info.status ?? 'unknown'),
      kynode_tax_type: String(info.taxType ?? ''),
    },
  });

  return { customer, verify: verifyBody };
}

Python (stripe library)

import os
from datetime import datetime, timezone

import requests
import stripe

stripe.api_key = os.environ['STRIPE_SECRET_KEY']
KYNODE_KEY = os.environ['KYNODE_API_KEY']

def verify_and_create_customer(business_number: str, start_date: str, owner_name: str, email: str):
    r = requests.post(
        'https://kynode-api.kynode.workers.dev/v1/verify',
        headers={'Authorization': f'Bearer {KYNODE_KEY}'},
        json={
            'business_number': business_number.replace('-', ''),
            'startDate': start_date,
            'ownerName': owner_name,
            'company_name': '노드메트릭스',
            'language': 'en',
        },
        timeout=30,
    )
    try:
        verify = r.json()
    except ValueError as e:
        raise RuntimeError('Invalid JSON from Kynode') from e

    if not r.ok:
        raise RuntimeError(verify.get('message', f'Kynode error {r.status_code}'))

    if not verify.get('isValid'):
        raise ValueError('Business could not be verified')

    info = verify['businessInfo']
    customer = stripe.Customer.create(
        email=email,
        name=info.get('companyName'),
        metadata={
            'business_number': info['businessNumber'],
            'kynode_verified_at': datetime.now(timezone.utc).isoformat(),
            'kynode_status': str(info.get('status', 'unknown')),
            'kynode_tax_type': str(info.get('taxType', '')),
        },
    )
    return customer, verify

Storing verification data

Store the canonical business number, verification timestamp, and Kynode status in `Customer.metadata` (keys are strings; keep values short). For heavy payloads or internal analytics, mirror the same record in your database with a foreign key to `stripe_customer_id`.

Never put API keys or full webhook secrets in metadata—only business-safe fields your support team may read in the Stripe Dashboard.

Payment flow best practices

  • Verify with Kynode before the first successful payment or before raising limits.
  • Cache verification results with a TTL; do not call verify on every card charge.
  • Re-verify periodically or when the customer updates registration data.
  • If verification fails, block payment capture and guide the user to fix data or contact support.

Kynode webhooks + Stripe

Listen for Kynode webhook events to update `Customer.metadata` when verification status changes (for example from pending to completed). Optionally use Stripe webhooks (`customer.updated`) to keep your database in sync—treat Kynode as the source of truth for registry facts.

Production checklist

  • Run Kynode and Stripe calls only on your backend; keep keys in a secret manager.
  • Use idempotency keys on Stripe writes when retrying after network failures.
  • Log correlation IDs across Kynode verify, your DB, and Stripe object IDs.
  • Monitor verification failure rates separately from payment failure rates.