JPay SDK v1.0

Embed Stripe payment link generation into any web project with a single script tag.

Quick Start

Three steps to generate a Stripe payment link from any HTML page:

<!-- 1. Include the SDK -->
<script src="https://jpaypayment.web.app/jpay.js"></script>

<script>
// 2. Configure (once, on page load)
JPay.init({
  companyId: 'YOUR_COMPANY_DOC_ID',   // Firestore companies doc ID
});

// 3. Create a payment link
async function chargeClient() {
  const { url } = await JPay.createPaymentLink(
    150,                // amount in dollars
    'client-uid-123',  // your reference (customer ID, order ID, etc.)
    {
      productName: 'Consultation Fee',
      type:        'one-time',
      currency:    'usd',
    }
  );
  // url → https://buy.stripe.com/xxx
  window.open(url);
}
</script>
ℹ️
Find your companyId in the JPay Admin page (jpaypayment.web.app) — click the + button and look at the ID column next to your company name.

Installation

Browser (script tag)

<script src="https://jpaypayment.web.app/jpay.js"></script>

Exposes a global JPay object. No build step required.

ES Module

// The same file works as an ES module — wrap in your own module
import 'https://jpaypayment.web.app/jpay.js';
// JPay is now on window.JPay

Node.js / non-browser

Use the REST API directly (see REST API section). The jpay.js file uses window.fetch so it targets browsers, but the underlying Cloud Functions accept any HTTP client.

JPay.init( config )

Set defaults so you don't have to repeat them in every call. Call once on page load, before any other JPay function.

JPay.init({ apiKey?: string, companyId?: string }) → void
FieldTypeRequiredDescription
companyId string optional Default Firestore companies doc ID. Used when payDetails.companyId is omitted.
apiKey string optional API key if you have configured one (see API Key Setup). Leave empty if not set up.

JPay.createPaymentLink( amount, referenceUID, payDetails )

Creates a Stripe Payment Link, saves the record to Firestore, and returns the hosted checkout URL. The customer visits the URL to complete payment — Stripe handles the checkout page.

JPay.createPaymentLink(
  amount: number,
  referenceUID: string,
  payDetails: PayDetails
)Promise<CreateResult>

Parameters

ParameterTypeRequiredDescription
amount number required Amount in dollars. Minimum 0.50. e.g. 150 = $150.00
referenceUID string required Your internal reference — a customer UID, order ID, invoice number, etc. Stored on the Firestore record and used to look up links later.
payDetails object required Payment configuration. See fields below.

payDetails fields

FieldTypeRequiredDefaultDescription
companyId string optional* JPay.init default Firestore companies doc ID. Falls back to value set in JPay.init().
targetDocId string optional referenceUID Write-back target. The exact document ID in the requesting project's collection that JPay updates when the payment completes. Defaults to referenceUID. See Cross-Project Write-Back.
productId string optional† Existing Stripe product ID (e.g. 'prod_Qx1234...'). Use this to attach the link to an existing product in your Stripe catalog.
productName string optional† Name of a new product to create in Stripe. Required if productId is not provided.
productDescription string optional Description shown on the Stripe checkout page. Only applies when creating a new product.
type string optional 'one-time' 'one-time' for a single charge, 'subscription' for recurring monthly billing.
currency string optional 'usd' ISO currency code: 'usd', 'cad', 'eur', 'gbp', 'aud', etc.
note string optional Internal note stored in Firestore. Not shown to the customer. Useful for invoice numbers or staff notes.

† Either productId or productName must be provided.

Returns — CreateResult

Promise resolves to
successtrue on success
urlThe Stripe Payment Link URL — send this to your customer
linkIdFirestore document ID of the saved link record
referenceUIDThe referenceUID you passed in (echoed back)

Example — existing product

const result = await JPay.createPaymentLink(
  250,
  customer.uid,
  {
    productId: 'prod_Qx9AbcDef123',  // existing Stripe product
    type:      'one-time',
    currency:  'usd',
    note:      `Invoice #${invoiceNum}`,
  }
);

console.log(result.url);     // https://buy.stripe.com/xxx
console.log(result.linkId);  // Firestore doc ID

Example — new product

const { url, linkId } = await JPay.createPaymentLink(
  99,
  order.id,
  {
    productName:        'Monthly Support Plan',
    productDescription: 'Includes unlimited email support and monthly check-in call.',
    type:               'subscription',
    currency:           'cad',
  }
);

// Open the checkout page in a new tab
window.open(url, '_blank');

Example — full error handling

async function generateLink(customerId, amount) {
  try {
    const { url } = await JPay.createPaymentLink(amount, customerId, {
      productName: 'Service Fee',
      type:        'one-time',
    });
    navigator.clipboard.writeText(url);
    alert('Link copied to clipboard!');
  } catch (err) {
    alert('Error: ' + err.message);
  }
}

Fetches all payment links that were previously created for a given referenceUID. Use this to check payment status or display link history for a customer record.

JPay.getPaymentLinks(referenceUID: string)Promise<GetResult>

Returns — GetResult

Promise resolves to
successtrue on success
referenceUIDThe referenceUID that was queried
linksArray of link records (see fields below). Newest first.

Link record fields

Payment record fields (inside payments[])

Example

const { links } = await JPay.getPaymentLinks(customer.uid);

if (links.length === 0) {
  console.log('No payment links for this customer');
}

for (const link of links) {
  console.log(link.url, link.paymentsCount, link.amount);

  for (const payment of link.payments) {
    console.log(payment.customerEmail, payment.paidAt);
  }
}

REST API — POST /createPaymentLinkAPI

The underlying Cloud Function endpoint. Use this from Node.js, PHP, Python, or any server that can make HTTP requests.

POST https://us-central1-jpay-a15a4.cloudfunctions.net/createPaymentLinkAPI

Request body (JSON)

{
  "apiKey":       "your-key",          // optional if no key configured
  "amount":       150,
  "referenceUID": "customer-or-order-uid",
  "targetDocId":  "invoiceDocId",          // optional — doc to update on payment (defaults to referenceUID)
  "payDetails": {
    "companyId":          "firestoreCompanyDocId",
    "type":               "one-time",          // or "subscription"
    "currency":           "usd",
    "productId":          "prod_xxx",          // OR productName below
    "productName":        "Service Fee",
    "productDescription": "Optional description",
    "note":               "Invoice #42"
  }
}

Success response

{
  "success":      true,
  "url":          "https://buy.stripe.com/xxx",
  "linkId":       "firestoreDocId",
  "referenceUID": "customer-or-order-uid"
}

Example — PHP

$response = file_get_contents('https://us-central1-jpay-a15a4.cloudfunctions.net/createPaymentLinkAPI', false, stream_context_create([
  'http' => [
    'method'  => 'POST',
    'header'  => 'Content-Type: application/json',
    'content' => json_encode([
      'amount'       => 150,
      'referenceUID' => $customer['uid'],
      'payDetails'   => [
        'companyId'   => 'yourCompanyDocId',
        'productName' => 'Service Fee',
      ],
    ]),
    'ignore_errors' => true,
  ]
]));
$data = json_decode($response, true);
$url  = $data['url'];

Example — Node.js

const res  = await fetch('https://us-central1-jpay-a15a4.cloudfunctions.net/createPaymentLinkAPI', {
  method:  'POST',
  headers: { 'Content-Type': 'application/json' },
  body:    JSON.stringify({ amount: 150, referenceUID: customerId, payDetails: { companyId, productName: 'Fee' } }),
});
const { url } = await res.json();

REST API — GET /getPaymentLinksByRef

GET https://us-central1-jpay-a15a4.cloudfunctions.net/getPaymentLinksByRef

Query parameters

ParamRequiredDescription
referenceUIDrequiredThe UID to look up
apiKeyoptionalAPI key, if configured
GET .../getPaymentLinksByRef?referenceUID=customer-uid-123&apiKey=yourkey

Cross-Project Write-Back

JPay can automatically update the requesting project's own Firestore the moment a payment completes — so the project that asked for the link doesn't have to poll. When a customer pays, JPay opens the requesting project (using its service account) and writes the paid status into the document you point it at.

How the loop closes

1. Your project  → POST createPaymentLinkAPI { amount, referenceUID, targetDocId, payDetails.companyId }
2. JPay          → creates Stripe link, stores paymentLinks doc (with companyId + targetDocId)
3. JPay → you    → returns { url }.  You send it to the customer.
4. Customer pays → Stripe fires checkout.session.completed → POST /stripeWebhook
5. JPay          → records payment, then opens YOUR project via its service account
6. JPay          → updates  <targetCollection>/<targetDocId>  with the paid status

1. Configure the company (one-time)

In the JPay Admin page → Manage Companies, each company that needs write-back must store two extra things:

Existing companies can be updated with the new Edit button. Leaving the Stripe key or service account blank when editing keeps the existing value.

2. Send the target doc ID per request

The requesting project owns the identity. Pass the doc ID of the record you want flagged as paid — as targetDocId, or simply as referenceUID (JPay uses referenceUID when targetDocId is omitted). No ID matching between JPay and your project is needed.

await JPay.createPaymentLink(150, 'VD-314277', {
  companyId:   'yourCompanyDocId',
  productName: 'Invoice VD-314277',
  targetDocId: 'VD-314277',   // → updates invoices/VD-314277 on payment
});

3. What JPay writes into your document

On payment, JPay set(..., { merge: true })s these fields onto <targetCollection>/<targetDocId> — your existing fields are preserved:

{
  "jpayPaid":          true,
  "jpayStatus":        "paid",
  "jpayAmount":        150.00,
  "jpayCurrency":      "usd",
  "jpayCustomerEmail": "customer@example.com",
  "jpayCustomerName":  "Jane Doe",
  "jpayPaidAt":        "2026-09-02T17:05:03.000Z",
  "jpayPayment":       { /* full payment record */ },
  "jpayUpdatedAt":     "2026-09-02T17:05:04.000Z"
}
JPay stamps writeBackStatus: 'ok' (or 'error' + writeBackError) back on its own paymentLinks doc, so you can confirm delivery.
⚠️
The doc ID must match a real record. Because the write uses merge, an unknown targetDocId creates a new doc with only the jpay* fields. Always send the ID of your existing record. Also: Stripe webhooks are per-mode — a sk_test_ key needs the webhook registered under Stripe Test mode too, or nothing is written back.

Firestore Schema

Collection: paymentLinks

Every generated link is stored here. Readable in the JPay Admin page.

Collection: companies

API Key Setup

By default the endpoints are open to any caller — fine for internal projects. To require an API key:

1. Set the key (run once in your terminal)

firebase functions:config:set jpay.api_key="choose-any-secret-string" --project jpay-a15a4
firebase deploy --only functions --project jpay-a15a4

2. Pass the key in your project

JPay.init({
  apiKey:    'choose-any-secret-string',
  companyId: 'yourCompanyDocId',
});
⚠️
If you embed the API key in client-side JavaScript it is visible in the browser. For higher security, make the createPaymentLinkAPI call from your server (PHP/Node) and never expose the key in the browser.

Webhook — Customer Data After Payment

Stripe hosts the checkout page, so you don't see card details — that's PCI compliance. But after each successful payment, Stripe POSTs a checkout.session.completed event to your webhook endpoint, which captures the customer's information and stores it in the payments[] array on the Firestore record.

The webhook is already configured on your Stripe account pointing to the Cloud Function. No action needed.

Webhook URL

POST https://us-central1-jpay-a15a4.cloudfunctions.net/stripeWebhook

Data captured per payment

↩️
After recording the payment, the webhook also pushes the paid status back into the requesting project's Firestore when the company is configured for it — see Cross-Project Write-Back.

Error Handling

Both createPaymentLink() and getPaymentLinks() throw a standard Error on failure. Always wrap calls in try/catch.

try {
  const { url } = await JPay.createPaymentLink(amount, uid, payDetails);
  // success
} catch (err) {
  // err.message contains a human-readable description
  console.error(err.message);
}

Common error messages

MessageCause
payDetails.companyId is requiredNo companyId in payDetails and no default set via JPay.init()
payDetails.productId or payDetails.productName is requiredNeither was provided
Minimum amount is $0.50Amount below Stripe's minimum
Company not found: xxxThe companyId doesn't exist in Firestore
Invalid API keyAPI key mismatch (only when a key is configured)

JPay SDK · powered by Stripe + Firebase · jpaypayment.web.app