# Sellkit Checkout Integration — AI Agent Prompt

Copy this prompt and give it to any AI coding assistant (Claude, ChatGPT, Cursor, etc.) along with your requirements. The AI will build a checkout page for your site.

---

## Prompt

```
Build a checkout page for my website using the Sellkit SDK.

## Sellkit SDK

Load the SDK:
<script src="https://sk-api.vibery.app/embed/sdk.js"></script>

The SDK exposes a `Sellkit` class:

### Initialize
var sk = new Sellkit('https://sk-api.vibery.app');

### Load product
var data = await sk.getProduct(tenantSlug, productSlug);
// Returns: { tenant_name, product: { name, slug, variants: [{ id, name, price, currency, meta }] }, mode, checkout_note }
// price is in smallest currency unit: VND as-is (299000 = 299.000₫), USD in cents (999 = $9.99)

### Load sale (multi-product collection)
// Fetch via: GET https://sk-api.vibery.app/api/v1/public/{tenantSlug}/sale/{saleSlug}
// Returns: { sale: { id, name, slug, description, product_ids }, tenant_name, products: [...], mode, checkout_note }

### Preview pricing (IMPORTANT — call on email blur)
var pricing = await sk.previewPricing({
  tenant_slug: 'TENANT_SLUG',
  variant_ids: ['variant-uuid-1', 'variant-uuid-2'],  // array for bundle
  email: customerEmail,        // triggers membership-based pricing
  coupon_code: 'CODE',         // optional
  quantity: 1
});
// Returns: { subtotal, total, discount, adjustments: [{rule_name, amount}], coupon_discount, coupon_error, tax_amount, tax_inclusive, currency }
// Call this whenever: email changes, product selection changes, coupon applied

### Create checkout
var order = await sk.checkout({
  tenant_slug: 'TENANT_SLUG',
  variant_ids: ['variant-uuid-1'],  // single or multiple
  name: customerName,
  email: customerEmail,
  phone: customerPhone,             // optional
  coupon_code: 'CODE',              // optional
  ref: 'affiliate-code',            // optional, from URL ?ref=xxx
  sale_id: 'sale-uuid',             // optional, for analytics
  tax_info: {                       // optional, for invoice
    company_name: 'Company',
    tax_code: '0123456789',
    address: '123 Street'
  }
});
// Returns: { order_id, order_number, total, currency, status, is_free, checkout_url, simulate }

### After checkout — INLINE payment (do NOT redirect to external domain):
if (order.is_free) {
  // Show success immediately — replace form with success message
  showSuccess(order);
} else {
  // Show payment inline on the SAME page — no redirect!
  // Detect QR code (SePay/VietQR bank transfer):
  var isQR = order.checkout_url && (order.checkout_url.includes('qr') || order.checkout_url.includes('sepay') || order.checkout_url.includes('vietqr'));

  if (isQR) {
    // Show QR code image inline — replace the form with payment screen
    // <img src="{order.checkout_url}" /> + order number + amount
  } else if (order.checkout_url) {
    // Stripe or other hosted checkout — open in new tab, keep current page for polling
    window.open(order.checkout_url, '_blank');
  }

  // IMPORTANT: Start polling immediately after showing payment
  sk.pollStatus(order.order_id, function(status) {
    if (status.status === 'paid' || status.status === 'fulfilled') {
      // Replace payment screen with success message
      showSuccess(status);
      // status.fulfillment = { license_key, download_url, ticket_code }
    }
    if (status._timeout) {
      // 30 min timeout — show "session expired, try again" message
      showExpired();
    }
  });
}

// The user stays on YOUR site the entire time. No domain switch.
// Flow: form → QR/payment screen → success — all inline.

// IMPORTANT: After checkout, set the order ID in the URL hash so the page
// survives reload and can be shared to another device:
window.history.replaceState(null, '', '#order=' + order.order_id);

// On page load, check for #order=xxx and resume:
var hashOrder = window.location.hash.match(/order=([a-f0-9-]+)/);
if (hashOrder) {
  var status = await sk.getOrderStatus(hashOrder[1]);
  if (status.status === 'paid') showSuccess(status);
  else if (status.status === 'pending') showPaymentScreen(status);
  else showForm(); // expired/canceled — back to form
}

### Format price helper
Sellkit.formatPrice(299000, 'vnd')   // "299.000 ₫"
Sellkit.formatPrice(999, 'usd')      // "$9.99"

## Key behaviors to implement:
1. On email input blur → call previewPricing() to show personalized price
2. On product selection change → debounce 400ms then call previewPricing()
3. Show pricing breakdown when discounts apply (rule names + amounts)
4. Show "Đã bao gồm VAT" when tax_inclusive is true, "+VAT" when false
5. Coupon: show apply button, display error or discount amount
6. After checkout: show payment INLINE (QR code + polling) — NEVER redirect to another domain
7. Support ?ref=xxx URL param for affiliate tracking
8. Three screens on the SAME page: form → payment (QR/link + polling spinner) → success
9. Detect QR by checking if checkout_url contains 'qr', 'sepay', or 'vietqr' → show as <img>
10. For Stripe URLs: open in new tab, keep current page for status polling

## My configuration:
- Tenant slug: YOUR_TENANT_SLUG
- Product slugs: YOUR_PRODUCT_SLUG (or sale slug for multi-product)
- Checkout domain: https://sellkit.vibery.app (for payment redirect)
- API base: https://sk-api.vibery.app

## My requirements:
[DESCRIBE YOUR DESIGN, LAYOUT, AND SPECIFIC NEEDS HERE]
```

---

## Quick reference

| Asset | URL |
|-------|-----|
| SDK | https://sk-api.vibery.app/embed/sdk.js |
| Embed widget | https://sk-api.vibery.app/embed/checkout.js |
| Sale page template | https://sk-api.vibery.app/embed/sale-page.html |
| Integration guide | https://sk-api.vibery.app/embed/integration-guide.md |
| This prompt | https://sk-api.vibery.app/embed/ai-prompt.md |
| Swagger docs | https://sk-api.vibery.app/docs |

## Notes for AI agents
- All prices are in the currency's **smallest unit** (VND as-is, USD in cents). Display with `Sellkit.formatPrice()`.
- The SDK handles all fetch calls. No need to write raw fetch.
- `previewPricing()` is the key function — it calculates personalized pricing based on email (membership), selected items (bundle rules), and coupons.
- Checkout domain for payment redirect is separate from API domain.
- No API key needed. All endpoints are public.
