# Welcome to the Flash Documentation!

Flash is a Bitcoin Lightning payments gateway that ensures every payment is processed in a **self-custodial** and **peer-to-peer (P2P)** manner. Here's how it works: when you receive a payment through Flash, the funds are sent **directly to your wallet**, maintaining your full control and ownership.

To simplify operations, Flash's fees are handled automatically. Once a payment is completed, your wallet will initiate a small transaction to pay Flash its fee. This happens transparently in the background, ensuring you can focus entirely on growing your business without worrying about manual processes or hidden charges.

Our platform offers tools like **Flashes**, **subscriptions**, and **public APIs** to power your monetization needs. This documentation will guide you through integrating these features into your projects with ease, helping you create seamless and secure payment experiences while keeping full custody of your funds.

### Jump right in

<table data-view="cards"><thead><tr><th></th><th></th><th data-hidden data-card-cover data-type="files"></th><th data-hidden></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><strong>Flashes</strong></td><td>Flexible, secure payment tools</td><td><a href="/files/UHF7zjwcq36zmrwja3ub">/files/UHF7zjwcq36zmrwja3ub</a></td><td></td><td><a href="/pages/2VHHOTnUfvgQCt0yBQTJ">/pages/2VHHOTnUfvgQCt0yBQTJ</a></td></tr><tr><td><strong>Subscriptions</strong></td><td>Manage recurring payments easily</td><td><a href="/files/vxs1ER5I8VkIA0IaOqfo">/files/vxs1ER5I8VkIA0IaOqfo</a></td><td></td><td><a href="/pages/cEHgDv7NGBquDGHeLyQ4">/pages/cEHgDv7NGBquDGHeLyQ4</a></td></tr><tr><td><strong>Integrations</strong></td><td>Seamless integration made simple</td><td><a href="/files/UHF7zjwcq36zmrwja3ub">/files/UHF7zjwcq36zmrwja3ub</a></td><td></td><td><a href="/pages/V790yCeT54I38H8gkYym">/pages/V790yCeT54I38H8gkYym</a></td></tr></tbody></table>


# Understanding Bitcoin Payments


# Set Up Your Account


# Connect Your Wallet

## Why Do I Need a Wallet to Use Flash?

A Bitcoin wallet is essential to using Flash because it serves as the hub for receiving, storing, and managing your Bitcoin payments—much like a bank account does for traditional currencies. Here are the key reasons why a wallet is required:

1. **Receive Payments** When customers make payments using Bitcoin, the funds are directly sent to your wallet. Flash connects to your wallet to ensure smooth and secure transactions.
2. **Security and Ownership** With a wallet, you retain full control and ownership of your Bitcoin. Flash doesn’t hold your funds, ensuring a decentralized and secure experience.
3. **Transaction Management** Your wallet provides a record of all incoming and outgoing transactions, enabling easy tracking and reconciliation of your payments.
4. **Instant Access to Funds** Bitcoin payments are processed instantly and are accessible in your wallet without intermediaries or delays.

***

## Which Wallets Can I Use with Flash?

Flash can only be used with wallets that support the **Nostr Wallet Connect** technology. The following wallets are currently compatible:

<table data-view="cards"><thead><tr><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><strong>Flash Wallet</strong></td><td>Flash's dedicated mobile wallet, the easiest and most complete solution.</td><td><a href="/pages/VnsgHMbYiv9FU6CKPhQv">/pages/VnsgHMbYiv9FU6CKPhQv</a></td></tr><tr><td><strong>Coinos</strong></td><td>A desktop-friendly wallet that is simple to set up and easy to use.</td><td><a href="/pages/dwqsMnIgJdaDwHdCqP9b">/pages/dwqsMnIgJdaDwHdCqP9b</a></td></tr><tr><td><strong>Alby</strong></td><td>A premium service with advanced wallet functionality and a suite of Bitcoin tools.</td><td></td></tr></tbody></table>

***

## How to Connect My Wallet to Flash?

Click one of the tiles above to read specific instructions for each wallet. If you're using a different wallet, here are some generic instructions:

1. **Make sure your wallet is NWC compatible**\
   Verify that the wallet supports Nostr Wallet Connect technology.
2. **Find your NWC string on that wallet**\
   Locate the unique NWC string provided by your wallet for integration.
3. **Paste it in Flash when creating a new wallet**\
   In the [Flash’s wallet setup interface](https://app.paywithflash.com/my-account?tab=Wallets), create a new wallet and select the *Nostr Wallet Connect* options. Enter the NWC string to connect your wallet.

   <figure><img src="/files/JXAZIgjRuaFImrUwBTTl" alt="" width="350"><figcaption></figcaption></figure>
4. **Verify with a small transaction** \
   Send a small amount of Bitcoin to ensure your wallet is correctly linked and transactions are processed smoothly.


# Flash Wallet


# Coinos Wallet

Creating a Coinos wallet is one of the simplest ways to start using Flash. There’s no need to download any app, and the process is quick and straightforward. Follow these steps to set up your Coinos wallet and link it to Flash.

## Step 1: Create a Coinos Wallet

1. **Access Coinos**\
   Click [here](https://coinos.io/register) to visit the Coinos registration page.
2. **Register Your Account**\
   Choose a username and password, then click **Save**. Your wallet will be created instantly.

***

## Step 2: Get Your NWC String

1. **Open Wallet Settings**\
   On your Coinos wallet homepage, click the gear icon (⚙️).

<figure><img src="/files/nbJRN01VLgAayYJZmYB3" alt="" width="375"><figcaption></figcaption></figure>

2. **Reveal Your NWC String**\
   Select **Reveal NWC**, and copy the string of letters and numbers displayed. This is your unique NWC string.

<figure><img src="/files/6reIStxPfUtN5WcFCqAf" alt="" width="375"><figcaption></figcaption></figure>

***

## Step 3: Connect to Flash

1. **Add Wallet in Flash**\
   In the [Flash’s wallet setup interface](https://app.paywithflash.com/my-account?tab=Wallets), create a new wallet and give it a name, such as "John Coinos."
2. **Select NWC Option**\
   Choose the Nostr Wallet Connect option in the setup interface.

<figure><img src="/files/JXAZIgjRuaFImrUwBTTl" alt="" width="350"><figcaption></figcaption></figure>

3. **Paste Your NWC String**\
   Enter the NWC string copied from your Coinos wallet.

<figure><img src="/files/WAVKPAFZIxMacjbAX0Xs" alt="" width="350"><figcaption></figcaption></figure>

4. **Complete Setup**\
   Test the connection to ensure everything works, and your wallet is ready to use!


# Alby Wallet


# Checkouts

## **Checkouts Overview**

Checkouts are a set of versatile tools designed to enable flexible monetization for your content. Unlike traditional subscriptions, product sales, or e-commerce integrations, Flashes allow you to create unique payment solutions tailored to your specific needs. Each Flash operates independently, making them perfect for diverse use cases.

***

## **Available Checkout Types**

<table data-view="cards"><thead><tr><th></th><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th><th data-hidden data-card-cover data-type="files"></th></tr></thead><tbody><tr><td></td><td><img src="/files/DvLZIzPovDlnQOcthkqC" alt="" data-size="line"><strong>Payment Links</strong></td><td>Create shareable URLs that allow users to pay easily by selecting their preferred payment method and amount.</td><td><a href="/pages/tbBF5m1nvBkvWeRLWrH5">/pages/tbBF5m1nvBkvWeRLWrH5</a></td><td></td></tr><tr><td></td><td><img src="/files/xV8Cu9EDssrAN8KFPtMh" alt="" data-size="line"><strong>Donation Widgets</strong></td><td>Add a floating, customizable call-to-action on your website to encourage donations or user contributions.</td><td><a href="/pages/TFXxtWDDAWNGpxhTvyIz">/pages/TFXxtWDDAWNGpxhTvyIz</a></td><td></td></tr><tr><td></td><td><img src="/files/uDYRBeKNuIK467xyeWr8" alt="" data-size="line"><strong>Page Paywalls</strong></td><td>Secure content behind a paywall, requiring users to pay for access to premium webpages. Ideal for articles, guides, or any content that needs restricted access.</td><td><a href="/pages/VqL5yxAjmp0jpZnR4Kg8">/pages/VqL5yxAjmp0jpZnR4Kg8</a></td><td></td></tr><tr><td></td><td><img src="/files/V0e6GNdE6Ja6IdI8pCrp" alt="" data-size="line"><strong>Video Paywalls</strong></td><td>Monetize video content by charging viewers based on watch time. Perfect for providing users with a pay-as-you-watch experience.</td><td><a href="/pages/0syWIpPtpqvPjOpdYSq4">/pages/0syWIpPtpqvPjOpdYSq4</a></td><td></td></tr><tr><td></td><td><p><img src="/files/Wl5R65SMNmT0JHuDKs4f" alt="" data-size="line"><strong>Payment Buttons</strong></p><p>Embed buttons on your site to trigger payments through a secure modal or redirect users post-payment.</p></td><td></td><td><a href="/pages/74snFdAhOham9GEHAv4x">/pages/74snFdAhOham9GEHAv4x</a></td><td></td></tr></tbody></table>

***

## **Why Choose Checkouts?**

* **Customizable**: Each Flash can be tailored to suit your content and audience.
* **Easy Integration**: Simply copy and paste snippets into your HTML or use with website builders.
* **Secure Payments**: All payments are processed directly to your wallet.
* **Flexible Use Cases**: From one-time payments to dynamic paywalls, Flashes adapt to your needs.

Flashes empower you to monetize your content effectively while keeping the process simple for both you and your users.


# Payment Links

## Get Started with Payment Links

Flash Payment Links offer a quick and convenient way to receive payments. These customizable URLs allow users to choose their preferred payment method and even set their own amount, making them ideal for flexible transactions.

> We are working on integrating invoices directly into the payment links - look out for future releases.

#### How It Works:

* **Customizable Links**: Define a default price that payers can adjust, ensuring flexibility for various payment scenarios.
* **Secure Transactions**: Payments are directly routed to your selected wallet.
* **Easy Sharing**: Share the link with others or embed it into buttons to streamline payment collection.

## How to Set Up a Payment Link

Follow the steps below to create your first payment link:

{% embed url="<https://scribehow.com/shared/Create_Payment_Link__yhnvLbk9Qji1zH0Yd81n-Q>" %}


# Donation Widget

## Get Started with the Donation Widget

The Flash Donation Widget lets you easily add a floating icon to your website with a custom call-to-action, encouraging users to support you. This highly visible widget is ideal for driving engagement and collecting payments seamlessly.

#### How It Works:

* **Customizable Messaging**: Set your call-to-action text, such as "Buy me a coffee," and personalize the widget to fit your needs.
* **Flexible Integration**: Use a simple script to embed the widget into any HTML page or website builder.
* **Secure Payments**: All payments are sent directly to your chosen wallet, ensuring a smooth and secure process.

## How to Set Up a Donation Widget

Follow the steps below to create your first payment widget:

{% embed url="<https://scribehow.com/shared/Create_Payment_Widget__hLiHnGKNRs26xKx-Y5Hl-Q>" %}


# Page Paywalls

## Get Started with Flash Page Paywalls

The Flash Page Paywall feature lets you easily monetize your content by charging visitors to access specific pages. It's a straightforward solution to secure and generate revenue from premium content on your website.

#### How It Works:

* Set a price and currency for accessing the page.
* Payments are securely processed and sent directly to your wallet.
* Embed the generated code snippet into your website or webpage to activate the paywall.

## How to Set Up a Page Paywall

Follow the steps below to create your first page paywall:

{% embed url="<https://scribehow.com/shared/Create_a_Paywall__68UGX-jcSEydPnJcY7KcoQ>" %}


# Payment Buttons

## Get Started with Payment Buttons

Flash Payment Buttons let you add a customizable payment option anywhere on your website. With a simple script, you can display a modal containing all the necessary payment information or redirect users to a specific page after successful payment.

#### How It Works:

* **Seamless Integration**: Add the button to any HTML page or website builder with an auto or manual snippet.
* **Customizable Options**: Define a default price, currency, and optional redirect after payment.
* **Secure Payments**: Transactions are sent directly to your selected wallet.

## How to Set Up a Payment Button

Follow the steps below to create your first payment button:

{% embed url="<https://scribehow.com/shared/Create_a_Payment_Button__6xMdrnjmT0iNWz76aueNNQ>" %}


# Video Paywalls

## Get Started with Flash Video Paywalls

The **Flash Video Paywall** feature allows you to easily monetize video content by charging users after a predefined number of seconds. This innovative pay-as-you-watch model is designed to maximize revenue without requiring upfront payment for the entire video.

**How it works:**

* You define a payment interval, e.g., every 60 seconds of video watching, the user is prompted to pay.
* Users only pay for the content they watch, providing a flexible experience.
* Integrate the video paywall by embedding the code snippet generated in the Flash dashboard.

## How to Set Up a Video Paywall

Follow the steps below to create your first video paywall:

{% embed url="<https://scribehow.com/shared/Create_a_Video_Paywall__MZIO0IBiTJ-wtYo9sw1pOg>" %}


# Subscriptions

Flash Subscriptions allow you to easily manage recurring payments using Bitcoin Lightning. This section will guide you through everything you need to know to create and manage subscriptions effectively on your platform.

With Flash Subscriptions, you can:

* **Create and manage subscription plans**: Learn how to set up subscription plans for your services or products, and generate a checkout page URL that users can visit to complete their subscription.
* **Pre-fill subscription forms**: Simplify the user experience by passing user data, such as **email**, **Nostr ID (npub)**, or a unique **external\_uuid**, through URL parameters. This allows the subscription form to be pre-filled, reducing friction for your users.
* **Authenticate to interact with the API**: Securely authenticate your application to interact with the subscription API. This will allow you to programmatically manage subscriptions, retrieve user details, and handle cancellations.
* **Utilize the subscription API**: Access Flash’s API to check subscription statuses, manage cancellations, and retrieve important user information. The API enables seamless integration with your platform.
* **Receive real-time updates via webhooks**: Stay informed of critical subscription events such as sign-ups, renewals, and payment failures by configuring webhooks that provide real-time notifications directly to your backend system.

***

#### **What You Will Learn:**

In the following pages, you'll be guided through:

1. **Creating a Subscription**: Step-by-step instructions on how to set up your subscription plan and generate a checkout page URL.
2. **Pre-filling User Data**: How to pre-fill subscription forms using URL parameters, reducing the steps for your users.
3. **Authentication**: Learn how to authenticate to use the Flash Subscription API securely.
4. **API Endpoints**: Access API endpoints for managing subscriptions, retrieving user details, and handling cancellations.
5. **Webhooks**: Set up and manage webhook notifications for real-time updates on subscription activity.


# Getting Started

Welcome to the **Flash Subscription API**! Flash makes it easy to manage and monetize recurring payments using Bitcoin Lightning. This is the starting point for creating your subscription plans, integrating APIs, and receiving real-time updates via webhooks.

## In this section

Learn how to:

* **Create your first Subscription Plan:** use the Flash web app to setup the parameters of your choice and get a checkout page url for you subscription plan.
* **Pre-Full User Details**: you can customise the checkout page url by passing user information (email, npub and external user id - you can pass your backend user id as well)&#x20;


# Create a Subscription Plan

## **Create Your First Subscription Plan**

The first step in using Flash subscriptions is creating a subscription plan. Flash provides a simple interface for setting up subscription plans, which you can share with users via a checkout URL, or even embed directly into your website.

Here’s a quick overview of what you can do:

1. **Create Subscription Plans:** Set up multiple subscription tiers with different pricing options.
2. **Embed Checkout Pages:** Use the subscription plan URL to redirect users to the payment page or embed it on your platform for a seamless experience.
3. **Manage Payments:** Track recurring payments and stay updated with webhook notifications.

Once your subscription is created, you will get:

* A **checkout URL** to share or embed.
* A **secret key** to authenticate webhook messages for real-time updates.

## **Follow this guide to create your first subscription:**

{% embed url="<https://scribehow.com/shared/Create_a_Subscription_Plan__UqqbUBOBTYKBmkdMOVmZtw>" %}

## **Use Cases After Setup**

After creating your subscription, you’ll be ready to:

* **Use the Flash Subscription API:** Retrieve subscription details, manage cancellations, and more.
* **Set up Webhook Integration:** Stay notified about key subscription events like sign-ups, renewals, and payment failures by integrating with our webhook system.

***

## **Next Steps:**

* **Prefill user detail:** Redirect users with more invormation to improve user experience
* **Authenticate**: Learn how to authenticate with the Subscription API.
* **Endpoints**: Review available API endpoints for managing subscriptions.
* **Webhooks**: Get real-time notifications for subscription events.

***

## **Embedding & Redirecting**

Once you have created your subscription, you can easily embed the checkout page or redirect users to it. This provides a seamless experience for users to subscribe to your service without needing to sign in beforehand. Simply copy the generated checkout URL and use it as needed.


# Pre-Fill User Details

The Flash Subscription allows you to streamline user experience by pre-filling subscription forms with data like **email**, **Nostr ID (npub)**, and a unique identifier, **external\_uuid**, when redirecting users to the subscription page. This saves users from re-entering details and enables you to integrate smoothly with your own user management systems.

## **URL Parameters**

When redirecting users to the Flash subscription page, you can pass relevant user information in the URL through a Base64-encoded JSON object. The system will automatically pre-fill the subscription form based on these values.

### **Parameters you can pass:**

* **email**: The user's email address.
* **npub**: The user's Nostr public key (Npub).
* **external\_uuid**: A unique identifier from your system to map users in your backend with Flash APIs. This value is not visible to users and is only used internally for identification.
* **is\_verified**: A boolean value indicating whether the user's email or Npub has already been verified.
  * If set to `false`, Flash will handle the verification process by either sending an email (for email addresses) or a Nostr DM (for Npubs) with a verification token.
  * If set to `true`, no additional verification steps are required, and the subscription process will continue seamlessly.

### **How to Pass Parameters**

You can pass the following parameters by embedding them as a Base64-encoded JSON object in the `params` query string:

```json
{
  "email": "user@example.com",
  "npub": "npub123...",
  "is_verified": false,
  "external_uuid": "your-unique-user-id"
}
```

The resulting URL might look like this:

{% code overflow="wrap" %}

```html
https://app.paywithflash.com/subscription-page?flashId=459&params=<Base64-encoded-JSON>
```

{% endcode %}

When Flash receives this URL, the system decodes the parameters and pre-populates the subscription form with the provided data.

## **How Flash Uses These Parameters:**

* **Pre-Filled Fields**: The user's **email** or **Npub** will be automatically filled into the form.
* **Verification**:
  * If the user is not verified (i.e., `is_verified = false`), Flash will initiate the verification process by sending a verification token:
    * **Email**: The user will receive a token via email.
    * **Npub**: The user will receive a token via Nostr DM.
  * The user will then input the received token into the interface to verify their identity.

> **Note:** The `external_uuid` parameter is designed for developers to map their own user IDs with the Flash system, making it easy to track and manage users across platforms. This value is not shown to users.

***

## **Example Workflow**

1. **Set Up the Redirect**: Pass the user's email, Npub, and external UUID in the URL as described above.
2. **Pre-Filled Form**: The subscription page will pre-fill the user's details.
3. **Verification (if required)**: If the user isn't verified, Flash sends a token via email or Nostr DM, which the user will input to complete verification.
4. **Subscription Completed**: Once verified, the user can complete their subscription without re-entering details.


# Authentication

{% hint style="success" %}
To ensure secure access, all requests to the Subscription API require an **Authorization** token. This token is unique for each subscription and must be included in the header of every API call. Here's how to authenticate your requests:
{% endhint %}

## **Getting Your Subscription API Token**

After you create a subscription in the Flash web app, a unique **Subscription API token** is automatically generated. You can always retrieve this token from the subscription edit screen. Make sure not to confuse this with the global API token—each subscription has its own token.

<figure><img src="/files/ygEywunQlrcrRvZJurCp" alt=""><figcaption></figcaption></figure>

***

## **How to Include the Token in Your Requests**

For every API request, the token must be included in the **Authorization** header, formatted as follows:

```graphql
Authorization: Bearer <Your-Subscription-API-Token>
```

Make sure to replace `<Your-Subscription-API-Token>` with the actual token you've obtained from the Flash web app.

**Token Example**

Here’s an example of how to structure your Authorization header in a typical API request:

```http
POST https://api.paywithflash.com/v1/get_user_subscription_details
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cC...
Content-Type: application/x-www-form-urlencoded
```

***

## **Error Handling**

* If the token is **missing** or **invalid**, you will receive a `401 Unauthorized` error.
* For expired tokens, you will get a `Token has expired` message.

### **Common Authentication Errors**

* **401 Unauthorized:** Occurs when the token is invalid, missing, or expired.
* **Invalid Token Format:** Ensure that the token is sent as `Bearer <token>` and is properly formatted.

## **Sample Request With Authentication**

```bash
curl -X POST https://api.paywithflash.com/v1/get_user_subscription_details \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cC..." \
-d "email=user@example.com&flash_id=12345"
```

***

## **Important Notes:**

* Never share your Subscription API token publicly.
* Make sure to use the correct subscription token for each individual subscription.


# Endpoints

{% openapi src="/files/3wo1AbevEHBuxTYRQHYA" path="/get\_user\_subscription\_details" method="post" %}
[Public\_Subscriptions\_openapi.yaml](https://3928471523-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTkqkw9wsKzfEEHWM3ZlI%2Fuploads%2FGUJ4Plzouym8Lw802h7N%2FPublic_Subscriptions_openapi.yaml?alt=media\&token=8c1c86f9-287b-48b8-a23d-ecde0de02706)
{% endopenapi %}

{% openapi src="/files/3wo1AbevEHBuxTYRQHYA" path="/cancel\_user\_subscription" method="post" %}
[Public\_Subscriptions\_openapi.yaml](https://3928471523-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FTkqkw9wsKzfEEHWM3ZlI%2Fuploads%2FGUJ4Plzouym8Lw802h7N%2FPublic_Subscriptions_openapi.yaml?alt=media\&token=8c1c86f9-287b-48b8-a23d-ecde0de02706)
{% endopenapi %}


# Webhooks

Webhook Integration Guide for Subscription Management

This guide is intended for developers looking to integrate real-time subscription event notifications into their applications. With our webhook system, you’ll receive automated POST requests to your designated URL for events like new sign-ups, renewals, and payment failures.

## **Getting Started**

A **webhook** allows our system to send real-time updates 📡 to your application. We’ll POST a JSON payload to your specified endpoint whenever a relevant subscription event occurs.

### **Step 1: Create/Edit Subscription & Plug in Your Webhook URL**

You can set up a webhook URL in the Flash web app either while creating a new subscription or by editing an existing subscription:

1. When **creating** a new subscription:
   1. Head over to **New Subs** > **Create a Subscription Plan**
   2. Make sure to click on the “**Use Advanced Webhook Features”** checkbox to make the Webhook Url field appear
   3. Input your **Webhook URL** in the form when creating the subscription<br>

      <figure><img src="/files/d4imEWmuIxXC1NB4oPNq" alt=""><figcaption></figcaption></figure>
   4. After creating the Subscription, you will receive a url with the subscription and the subscription key. You can use this key to decode and verify the JWT token that comes with our event (see guide below).
2. When you have **already created** a subscription:
   1. Head over to My Subscriptions
   2. Select the subscription you want to add webhooks for
   3. Click on “Edit Subscription”
   4. Fill in/modify the **Webhook Url** field
   5. The subscription key is also visible in this screen and can be used to decode and verify the JWT token that comes with our event (see guide below).<br>

      <figure><img src="/files/3D2Zbny3Nz1m46YfBSAM" alt=""><figcaption></figcaption></figure>

### **Step 2: Configure Your Webhook Endpoint**

To receive webhook notifications, set up a POST endpoint in your application:

1. Ensure it accepts JSON payloads.
2. Process the data based on the event type and take appropriate actions.

**Security Tip:** Always use HTTPS to secure webhook transmissions.

#### **Responding to Webhook Calls**

Your webhook endpoint should respond with a `200 OK` status to acknowledge receipt of the event. If no response or an error is received, we may retry the notification.

***

## **Event Types**

The system triggers the following events, each sending a POST request with event data to your webhook:

<details>

<summary>1. User Signed Up (name='user_signed_up', id=1)</summary>

Triggered when a user subscribes to a plan. The payload includes details like the user's public key, plan, and transaction info.

```json
{
  "data": {
    "public_key": "55a12716a6c4e8c95fc83dc046c3ea2209d3e3a1b87b15c48ef562b5a8599ed8",
    "external_uuid": "adf8cab9-add3-400d-b83c-9b68cfb6be8d",
    "name": "Jane Doe",
    "email": "jane.doe@example.com",
    "about": "Enthusiast of digital art",
    "picture_url": "<https://example.com/jane.jpg>",
    "user_plan": "Premium",
    "user_plan_id": "23",
    "signup_date": "2024-02-15",
    "next_payment_date": "2024-03-15",
    "failed_payment_date": "",
    "transaction_id": "654",
    "transaction_amount": "100",
    "transaction_currency": "Satoshis"
    "transaction_date": "2024-02-15"
  }
}
```

</details>

<details>

<summary>2. Renewal Successful (name='renewal_successful', id=2)</summary>

Sent when a user's subscription renewal is successfully processed.

```json
{
  "data": {
    "public_key": "55a12716a6c4e8c95fc83dc046c3ea2209d3e3a1b87b15c48ef562b5a8599ed8",
    "external_uuid": "adf8cab9-add3-400d-b83c-9b68cfb6be8d",
    "name": "Jane Doe",
    "email": "jane.doe@example.com",
    "about": "Enthusiast of digital art",
    "picture_url": "<https://example.com/jane.jpg>",
    "user_plan": "Premium",
    "user_plan_id": "24"
    "signup_date": "2024-02-15",
    "next_payment_date": "2024-04-15",
    "failed_payment_date": "",
    "transaction_id": "837",
    "transaction_amount": "100",
    "transaction_currency": "Satoshis",
    "transaction_date": "2024-03-15"
  }
}
```

</details>

<details>

<summary>3. Renewal Failed (name='renewal_failed', id=3)</summary>

Sent if a subscription renewal fails, typically due to a payment issue.

```json
{
  "data": {
    "public_key": "55a12716a6c4e8c95fc83dc046c3ea2209d3e3a1b87b15c48ef562b5a8599ed8",
    "external_uuid": "adf8cab9-add3-400d-b83c-9b68cfb6be8d",
    "name": "Jane Doe",
    "email": "jane.doe@example.com",
    "about": "Enthusiast of digital art",
    "picture_url": "<https://example.com/jane.jpg>",
    "user_plan": "Premium",
    "user_plan_id": "67",
    "signup_date": "2024-02-15",
    "next_payment_date": "",
    "failed_payment_date": "2024-03-15",
    "transaction_id": "-1",
    "transaction_amount": "100",
    "transaction_currency": "Satoshis",
    "transaction_date": ""
  }
}
```

</details>

<details>

<summary>4. Subscription Paused (name='user_paused_subscription', id=4)</summary>

Sent if a subscription renewal fails, typically due to a payment issue.

```json
{
  "data": {
    "public_key": "55a12716a6c4e8c95fc83dc046c3ea2209d3e3a1b87b15c48ef562b5a8599ed8",
    "external_uuid": "adf8cab9-add3-400d-b83c-9b68cfb6be8d",
    "name": "Jane Doe",
    "email": "jane.doe@example.com",
    "about": "Enthusiast of digital art",
    "picture_url": "<https://example.com/jane.jpg>",
    "user_plan": "Premium",
    "user_plan_id": "67",
    "signup_date": "2024-02-15",
    "next_payment_date": "",
    "failed_payment_date": "2024-03-15",
    "transaction_id": "-1",
    "transaction_amount": "100",
    "transaction_currency": "Satoshis",
    "transaction_date": ""
  }
}
```

</details>

<details>

<summary>5. Subscription Cancelled (name='user_cancelled_subscription', id=5)</summary>

Sent if a subscription renewal fails, typically due to a payment issue.

```json
{
  "data": {
    "public_key": "55a12716a6c4e8c95fc83dc046c3ea2209d3e3a1b87b15c48ef562b5a8599ed8",
    "external_uuid": "adf8cab9-add3-400d-b83c-9b68cfb6be8d",
    "name": "Jane Doe",
    "email": "jane.doe@example.com",
    "about": "Enthusiast of digital art",
    "picture_url": "<https://example.com/jane.jpg>",
    "user_plan": "Premium",
    "user_plan_id": "67",
    "signup_date": "2024-02-15",
    "next_payment_date": "",
    "failed_payment_date": "2024-03-15",
    "transaction_id": "-1",
    "transaction_amount": "100",
    "transaction_currency": "Satoshis",
    "transaction_date": ""
  }
}
```

</details>

***

## Webhook Payload Structure

The data sent to your webhook URL will be structured as a JSON object containing two primary fields: **`eventType`** and **`data`**. The **`eventType`** field indicates the type of event, while the **`data`** field contains event-specific information.

<details>

<summary>Here's a breakdown of the data you can expect for each event type:</summary>

* **Common Fields Across All Event Types:**
  * **`public_key`**: The user's public key.
  * **`name`**: The user's name.
  * **`email`**: The user's email address.
  * **`about`**: A brief description of the user.
  * **`picture_url`**: The URL to the user's profile picture.
  * **`user_plan`**: The subscription plan the user has signed up for.
  * **`user_plan_id`**: The subscription plan id the user has signed up for.
* **Event-Specific Fields:**
  * **`signup_date`**: The date the user signed up (only for **`user_signed_up`**).
  * **`next_payment_date`**: The next payment date for the subscription (only for **`renewal_successful`**).
  * **`failed_payment_date`**: The date of the failed payment (only for **`renewal_failed`**).
  * **`transaction_id`**: The ID of the transaction (varies by event type).
  * **`transaction_amount`**: The payment amount of the transaction(all same)
  * **`transaction_currency`**: The currency of the transaction(all same)
  * `transaction_date`: The date of the transaction (varies by event type).

</details>

***

## **Securing Webhooks with JWT**

All webhooks include a JWT token in the `Authorization` header, allowing you to verify that the event is coming from Flash. You can decode and verify this token using the subscription key provided in the web app.

Example token:

```json
{
  "version": "1.0",
  "eventType": {
    "id": "1",
    "name": "user_signed_up"
  },
  "user_public_key": "55a12716a6c4e8c95fc83dc046c3ea2209d3e3a1b87b15c48ef562b5a8599ed8",
  "exp": "2024-05-01T08:48:38Z"
}
```

**Token Expiration:** The token is valid for 1 hour.

To verify the token, decode it using your subscription key and the `HS256` algorithm.

***

## **Handling Webhooks**

Your webhook should always respond with a `200 OK` status after processing the event. Failure to do so may cause the system to retry the notification.

***

## **Best Practices**

* **Idempotency:** Ensure that your webhook handler can process the same event more than once without causing errors or duplicate actions.
* **Logging:** Implement logging for all webhook events to make troubleshooting easier.

***

## **FAQ**

**Can I set up multiple webhook URLs?**

<details>

<summary><strong>Can I set up multiple webhook URLs?</strong></summary>

Currently, we support one webhook URL per subscription. You can handle different events with routing logic in your app.

</details>

<details>

<summary><strong>What if I stop receiving webhooks?</strong></summary>

Ensure your endpoint is correctly configured and reachable. If the issue persists, contact our support team.

</details>

***

## Conclusion

By integrating our webhook system, you can stay updated on key subscription events in real time, helping automate and optimize your processes. Feel free to contact our developer support team if you need any help.

***

## Webhook Usage - full example

Assuming your webhook URL is `https://webhook` , you can refer to the following code:

{% tabs %}
{% tab title="JavaScript (NodeJS)" %}

```javascript
const jwt = require('jsonwebtoken');

app.post('/webhook', (req, res) => {
	const data = req.body;  // Extract JSON data from the request
	
	const authHeader = req.headers.authorization;
	const token = authHeader && authHeader.split(' ')[1];
	
	if (!token) {
	    return res.status(401).json({ error: 'No token provided' });
	}

	// Verify and decode the JWT token
	const secret = 'your_secret_key';
	jwt.verify(token, secret, (err, decodedToken) => {
	    if (err) {
	        return res.status(401).json({ error: 'Invalid token' });
	    }
	
	    const { version, eventType, user_public_key } = decodedToken;
	
	    // Process the data as needed
	    console.log('User public key:', user_public_key);
	    console.log('Event Type:', eventType);
	});
	
  // Access fields from the JSON payload
  const userPublicKey = data.data.public_key;
  const userName = data.data.name;
  const userEmail = data.data.email;
  
  // Process the data...
	
  res.json({ ok: true });
});

```

{% endtab %}

{% tab title="Python (Flask)" %}

```python
from flask import request, jsonify
import jwt

@app.route('/webhook', methods=['POST'])
def webhook():
    data = request.get_json()

    auth_header = request.headers.get('Authorization')
    token = auth_header.split(" ")[1] if auth_header else None

    if not token:
        return jsonify({'error': 'No token provided'}), 401

    secret = 'your_secret_key'
    try:
        decoded_token = jwt.decode(token, secret, algorithms=['HS256'])
        version = decoded_token.get('version')
        event_type = decoded_token.get('eventType')
        user_public_key = decoded_token.get('user_public_key')

        print(f"User public key: {user_public_key}")
        print(f"Event Type: {event_type}")

    except jwt.ExpiredSignatureError:
        return jsonify({'error': 'Token has expired'}), 401
    except jwt.InvalidTokenError:
        return jsonify({'error': 'Invalid token'}), 401

    user_public_key = data.get('data').get('public_key')
    user_name = data.get('data').get('name')
    user_email = data.get('data').get('email')
    
    return jsonify({'ok': True})

```

{% endtab %}

{% tab title="Ruby (Sinatra)" %}

```ruby
require 'json'
require 'jwt'

post '/webhook' do
  data = JSON.parse(request.body.read)

  auth_header = request.env['HTTP_AUTHORIZATION']
  token = auth_header.split(' ')[1] if auth_header

  halt 401, { error: 'No token provided' }.to_json unless token

  secret = 'your_secret_key'
  begin
    decoded_token = JWT.decode(token, secret, true, algorithm: 'HS256')[0]
    user_public_key = decoded_token['user_public_key']
    event_type = decoded_token['eventType']

    puts "User public key: #{user_public_key}"
    puts "Event Type: #{event_type}"
  
  rescue JWT::ExpiredSignature
    halt 401, { error: 'Token has expired' }.to_json
  rescue JWT::DecodeError
    halt 401, { error: 'Invalid token' }.to_json
  end

  user_public_key = data['data']['public_key']
  user_name = data['data']['name']
  user_email = data['data']['email']
  
  { ok: true }.to_json
end

```

{% endtab %}

{% tab title="Java (Spring Boot)" %}

```java
import org.springframework.web.bind.annotation.*;
import org.springframework.http.ResponseEntity;
import io.jsonwebtoken.*;
import java.util.Map;

@RestController
public class WebhookController {

    @PostMapping("/webhook")
    public ResponseEntity<?> handleWebhook(@RequestBody Map<String, Object> payload, 
                                           @RequestHeader("Authorization") String authHeader) {
        
        String token = authHeader != null ? authHeader.split(" ")[1] : null;
        if (token == null) {
            return ResponseEntity.status(401).body(Map.of("error", "No token provided"));
        }

        String secret = "your_secret_key";
        try {
            Jws<Claims> decodedToken = Jwts.parser()
                                            .setSigningKey(secret)
                                            .parseClaimsJws(token);
            
            String version = (String) decodedToken.getBody().get("version");
            Map<String, Object> eventType = (Map<String, Object>) decodedToken.getBody().get("eventType");
            String userPublicKey = (String) decodedToken.getBody().get("user_public_key");

            System.out.println("User public key: " + userPublicKey);
            System.out.println("Event Type: " + eventType);

        } catch (ExpiredJwtException e) {
            return ResponseEntity.status(401).body(Map.of("error", "Token has expired"));
        } catch (JwtException e) {
            return ResponseEntity.status(401).body(Map.of("error", "Invalid token"));
        }

        Map<String, Object> data = (Map<String, Object>) payload.get("data");
        String userPublicKey = (String) data.get("public_key");
        String userName = (String) data.get("name");
        String userEmail = (String) data.get("email");

        return ResponseEntity.ok(Map.of("ok", true));
    }
}

```

{% endtab %}

{% tab title="PHP" %}

```php
require 'vendor/autoload.php'; // Include JWT library (e.g., firebase/php-jwt)

use \Firebase\JWT\JWT;

$secret = 'your_secret_key';

$data = json_decode(file_get_contents('php://input'), true);

$authHeader = $_SERVER['HTTP_AUTHORIZATION'] ?? null;
if ($authHeader) {
    $token = explode(' ', $authHeader)[1];
} else {
    http_response_code(401);
    echo json_encode(['error' => 'No token provided']);
    exit;
}

try {
    $decodedToken = JWT::decode($token, $secret, ['HS256']);
    $decodedArray = (array) $decodedToken;

    $userPublicKey = $decodedArray['user_public_key'];
    $eventType = $decodedArray['eventType'];

    echo "User public key: $userPublicKey\n";
    echo "Event Type: $eventType\n";

} catch (Exception $e) {
    http_response_code(401);
    echo json_encode(['error' => 'Invalid token']);
    exit;
}

$userPublicKey = $data['data']['public_key'] ?? null;
$userName = $data['data']['name'] ?? null;
$userEmail = $data['data']['email'] ?? null;

echo json_encode(['ok' => true]);

```

{% endtab %}
{% endtabs %}


# Stores

### **Stores in Flash**

Flash enables businesses to create and manage their products seamlessly, offering features like product pages, a full online store, and an integrated point-of-sale (PoS) system. These tools are designed to help you sell your products, manage transactions, and accept payments with minimal effort.

This guide covers everything you need to know about the **Stores** section, including how to:

* **Individual Product Pages**: Each product you create gets its own checkout page with a unique URL.
* **Online Store**: A public store combining all your products, available via a shareable URL or embeddable code for your website.
* **Point of Sale (PoS)**: A versatile PoS system for physical stores or quick manual sales, supporting both custom values and pre-configured products.


# Managing Products

<figure><img src="/files/iTDpV24KYedGFneYv968" alt=""><figcaption></figcaption></figure>

## 1. **Creating Products**

You can create individual products with details like name, price, category, and inventory. Here’s how:

1. Navigate to **Products > My Products**.
2. Click on the **"Add New Product"** button.
3. Fill in the following fields:
   * **Name**: The name of your product.
   * **Price**: The price of the product in your preferred currency or satoshis.
   * **Category**: Group your products for better organization (e.g., Beverages, Electronics).
   * **Inventory**: Optional – Track the available quantity.
   * More fields are available
4. Add one or multiple images to make your product visually appealing.
5. Save the product.

Each product will automatically generate:

* A **Checkout Page URL** for direct sharing.
* A **Product Page URL** for customers to view details and make purchases.

## **2. Managing Existing Products**

In the **My Products** dashboard, you can:

* Edit product details.
* Check inventory status.
* View and copy checkout or product page URLs.

This gives you full control over your offerings, ensuring they’re always up-to-date.


# Hosted Store

The Hosted Store combines all your products into a single online storefront, accessible via a unique URL. Customers can browse and purchase multiple products at once.

<figure><img src="/files/sz8I5UOPVoPtsllO8Ckx" alt=""><figcaption></figcaption></figure>

## **1. Features of the Hosted Store**

* **Shareable URL**: Each business gets a unique store link.
* **Embeddable URL**: Easily integrate the store into your website.
* **Customizable Layout**: Products are automatically categorized based on your settings (e.g., Beverages, Electronics).

## **2. Setting Up Your Store**

1. Add products to your **My Products** list.
2. Navigate to **Products > My Store**.
3. Copy the store URL or embed code provided.

You can customize the store’s appearance and functionality in future updates.


# Point of Sale

The Point of Sale system is ideal for businesses with physical locations or those needing to process payments manually.&#x20;

## **Key Features**

* Connect multiple wallets to accept payments in Bitcoin or satoshis.
* Automatically calculate totals, including taxes (if applicable).
* Easily switch between manual and product-based transactions

## 2 modes available:

{% tabs %}
{% tab title="Basic Calculator" %}
For quick transactions where you manually input the amount:

1. Navigate to **Products > My PoS**.
2. Select the **"Point of Sale"** tab.
3. Use the calculator to enter a custom amount.
4. Generate an invoice to process the payment.

<figure><img src="/files/x18lILGycQQINzWbP8t2" alt=""><figcaption></figcaption></figure>
{% endtab %}

{% tab title="Product Selection" %}
For businesses selling pre-configured products:

1. Navigate to **Products > My PoS**.
2. Select the **"Products"** tab.
3. Choose products from the displayed list to add to the current order.
4. Adjust quantities as needed.
5. Generate an invoice with a breakdown of selected items.

<figure><img src="/files/6inSSZR27hYideJtQFnL" alt=""><figcaption></figcaption></figure>
{% endtab %}
{% endtabs %}


# Integrations

## **Integrations with Flash**

Welcome to the Flash Integrations documentation! Flash simplifies Bitcoin payments for your online store and streaming platforms, providing seamless integration with popular e-commerce platforms. With our plugins and tools, you can easily enable non-custodial Bitcoin payments, empowering your customers to transact via the Lightning Network.

Whether you're a small business or a large-scale enterprise, Flash has you covered with robust, easy-to-use solutions.

***

## **Supported Platforms and Integrations**

Explore our growing list of supported platforms and learn how to integrate Flash with your preferred e-commerce solution. Each guide provides detailed, step-by-step instructions for setup and configuration.

<table data-view="cards"><thead><tr><th></th><th></th><th></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td></td><td><strong>Woocommerce</strong></td><td>Enable Bitcoin payments in your WooCommerce store with the Flash plugin.<br></td><td><a href="/pages/wy8EGJoMSjJZ3qJcdjJG">/pages/wy8EGJoMSjJZ3qJcdjJG</a></td></tr><tr><td></td><td><strong>Shopify</strong></td><td>Learn how to set up the Flash plugin for Shopify to accept Bitcoin payments.</td><td><a href="/pages/ySWq6WZgVay6hrM2Szpx">/pages/ySWq6WZgVay6hrM2Szpx</a></td></tr><tr><td></td><td>Wix</td><td>Start using the Flash app in your Wix store to accept Bitcoin payments.</td><td><a href="/pages/kbFndjmlC5TGt33ZJmR2">/pages/kbFndjmlC5TGt33ZJmR2</a></td></tr></tbody></table>

***

## **Why Choose Flash for Your E-Commerce Store?**

* **Non-Custodial Payments:** Retain full control over your funds with our non-custodial payment gateway.
* **Lightning Network Support:** Leverage the power of instant and low-cost Bitcoin transactions.
* **Easy Integration:** Our plugins and APIs are designed for quick setup and seamless functionality.
* **Transparent Fees:** Pay only a small transaction fee with no hidden costs.

***

**Need Help?**

If you need assistance or have questions about Flash integrations, visit our **Support Center** or email us at **<support@paywithflash.com>**.


# WooCommerce

## **Setting Up the Flash Plugin for WooCommerce**

The Flash plugin for WooCommerce allows merchants to accept Bitcoin payments through their online store effortlessly. This guide will walk you through the setup process, including downloading the plugin, configuring your API key, and enabling the payment gateway.

***

## **Overview**

The Flash plugin integrates directly with WooCommerce to provide a secure and user-friendly Bitcoin payment solution. With this plugin, you can:

* Accept Bitcoin payments directly in your store.
* Ensure non-custodial transactions with full control of your funds.
* Enhance your store’s payment options with a cutting-edge Lightning Network solution.

***

## **Key Steps**

1. **Download the Plugin:**\
   Access the Flash plugin from your dashboard and download the zip file.
2. **Install the Plugin on WooCommerce:**\
   Upload the downloaded plugin file to your WooCommerce store and activate it.
3. **Configure Your API Key:**\
   Generate and input your unique API key to connect your store with the Flash payment gateway.
4. **Enable Flash Payments:**\
   Adjust your WooCommerce payment settings to activate the "Flash - Pay in Bitcoin" option.

{% embed url="<https://scribehow.com/shared/Set_Up_the_Flash_Plugin_for_WooCommerce__grcrOuabRBeQ3aaEoa6ZHg>" %}


# Shopify

## Setting Up the Flash Integration for Shopify

The Flash integration for Shopify enables merchants to accept Bitcoin payments seamlessly through their online stores. Unlike WooCommerce, the Shopify integration requires a custom setup due to Shopify's requirements for custom apps. This guide explains the process and why a call with our team is necessary to get you up and running efficiently.

***

## Overview

The Flash integration for Shopify provides a secure and user-friendly Bitcoin payment solution. Here's what it offers:

* Accept Bitcoin payments directly in your Shopify store.
* Ensure non-custodial transactions for full control of your funds.
* Provide your customers with cutting-edge payment options using the Bitcoin Lightning Network.

Due to Shopify’s policies, custom app installations involve several steps that require access to specific tools, such as a Shopify Partner Account. For this reason, our team will guide you through the installation process during a short 15-minute call.

***

## Why the Setup Process Requires a Call

Shopify requires custom apps like Flash to be installed via a Partner Account, which ensures security and compliance. This process involves several technical steps that are not yet self-service, including:

* Adding the Flash app to your Shopify store through a Shopify Partner Account.
* Configuring the app settings to match your store’s requirements.
* Connecting your Flash API key to enable Bitcoin payments.

Our team will handle these steps to ensure everything is set up correctly and quickly, saving you time and preventing potential errors.

***

## Key Steps

1. **Confirm Shopify Store Requirements**:\
   Ensure you have a Shopify store with a paid plan. Free or trial accounts are not eligible for custom app installations.
2. **Book a Call with Flash**:\
   Schedule a 15-minute onboarding call with our team using [this link](https://calendly.com/pierre-corbin-paywithflash/flash-shopify-integration).
3. **Join the Call with Our Team**:\
   During the call, our team will:
   * Add the Flash app to your store via a Shopify Partner Account.
   * Configure your store settings to integrate Flash seamlessly.
   * Guide you through connecting your Flash API key for Bitcoin payments.
4. **Test and Enable Flash Payments**:\
   Once the setup is complete, we'll perform a quick test to ensure everything works as expected. After that, you can start accepting Bitcoin payments in your Shopify store.

***

## Benefits of Booking a Call

* **Streamlined Installation**: Avoid the complexity of setting up a custom app on your own.
* **Expert Assistance**: Get real-time support from our team to ensure your store is configured correctly.
* **Faster Setup**: Complete the entire process in just 15 minutes.

***

## Additional Information

If you have any questions before booking the call, feel free to contact us at <support@paywithflash.com>.

Once set up, you’ll be equipped with the most advanced Bitcoin payment solution for Shopify, enabling seamless and secure transactions for your customers.


# Wix

## Setting Up the Flash Integration for Wix

The Flash integration for Wix enables merchants to accept Bitcoin payments seamlessly through their online stores. Unlike WooCommerce, the Wix integration requires a custom setup due to Wix's requirements for custom apps. This guide explains the process and why a call with our team is necessary to get you up and running efficiently.

***

## Overview

The Flash integration for Wix provides a secure and user-friendly Bitcoin payment solution. Here's what it offers:

* Accept Bitcoin payments directly in your Wix store.
* Ensure non-custodial transactions for full control of your funds.
* Provide your customers with cutting-edge payment options using the Bitcoin Lightning Network.

Due to Wix’s policies, custom app installations involve several steps that require access to specific tools, such as a Wix Partner Account. For this reason, our team will guide you through the installation process during a short 15-minute call.

***

## Why the Setup Process Requires a Call

Wix requires custom apps like Flash to be installed via a Partner Account, which ensures security and compliance. This process involves several technical steps that are not yet self-service, including:

* Adding the Flash app to your Wix store through a Wix Partner Account.
* Configuring the app settings to match your store’s requirements.
* Connecting your Flash API key to enable Bitcoin payments.

Our team will handle these steps to ensure everything is set up correctly and quickly, saving you time and preventing potential errors.

***

## Key Steps

1. **Confirm Wix Store Requirements**:\
   Ensure you have a Wix store with a paid plan. Free or trial accounts are not eligible for custom app installations.
2. **Book a Call with Flash**:\
   Schedule a 15-minute onboarding call with our team using [this link](https://calendly.com/pierre-corbin-paywithflash/flash-wix-integration).
3. **Join the Call with Our Team**:\
   During the call, our team will:
   * Add the Flash app to your store via a Wix Partner Account.
   * Configure your store settings to integrate Flash seamlessly.
   * Guide you through connecting your Flash API key for Bitcoin payments.
4. **Test and Enable Flash Payments**:\
   Once the setup is complete, we'll perform a quick test to ensure everything works as expected. After that, you can start accepting Bitcoin payments in your Wix store.

***

## Benefits of Booking a Call

* **Streamlined Installation**: Avoid the complexity of setting up a custom app on your own.
* **Expert Assistance**: Get real-time support from our team to ensure your store is configured correctly.
* **Faster Setup**: Complete the entire process in just 15 minutes.

***

## Additional Information

If you have any questions before booking the call, feel free to contact us at <support@paywithflash.com>.

Once set up, you’ll be equipped with the most advanced Bitcoin payment solution for Wix, enabling seamless and secure transactions for your customers.


# Welcome

Welcome to the Flash API documentation! Flash is a Bitcoin Lightning payments gateway enabling seamless subscription management, paywalls, and micropayments. This documentation will guide you through our public APIs, offering easy access to key functionalities to integrate with Flash. The API is designed with simplicity in mind to help developers quickly onboard and manage their integrations.

### Jump right in

<table data-view="cards"><thead><tr><th></th><th></th><th data-hidden data-card-cover data-type="files"></th><th data-hidden></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><strong>Authentication</strong></td><td>Get your credentials</td><td><a href="/files/UHF7zjwcq36zmrwja3ub">/files/UHF7zjwcq36zmrwja3ub</a></td><td></td><td><a href="/pages/CyH2xJQs9yWJ1S8BYNav">/pages/CyH2xJQs9yWJ1S8BYNav</a></td></tr><tr><td><strong>Subscriptions</strong></td><td>Setup your subscriptions</td><td><a href="/files/vxs1ER5I8VkIA0IaOqfo">/files/vxs1ER5I8VkIA0IaOqfo</a></td><td></td><td><a href="/pages/MWLokzqh6c2EW7NdljRz">/pages/MWLokzqh6c2EW7NdljRz</a></td></tr></tbody></table>


# Authentication

{% hint style="info" %}
Authentication is only setup for the subscription APIs.
{% endhint %}

* All API requests require a valid `Authorization` token in the header.
* Tokens are JWT and must be signed with the corresponding secret key associated with your Flash account.


# Subscriptions


# Subscription-specific Authentication

{% hint style="success" %}
To ensure secure access, all requests to the Subscription API require an **Authorization** token. This token is unique for each subscription and must be included in the header of every API call. Here's how to authenticate your requests:
{% endhint %}

## **Getting Your Subscription API Token**

After you create a subscription in the Flash web app, a unique **Subscription API token** is automatically generated. You can always retrieve this token from the subscription edit screen. Make sure not to confuse this with the global API token—each subscription has its own token.

<figure><img src="/files/ygEywunQlrcrRvZJurCp" alt=""><figcaption></figcaption></figure>

***

## **How to Include the Token in Your Requests**

For every API request, the token must be included in the **Authorization** header, formatted as follows:

```graphql
Authorization: Bearer <Your-Subscription-API-Token>
```

Make sure to replace `<Your-Subscription-API-Token>` with the actual token you've obtained from the Flash web app.

**Token Example**

Here’s an example of how to structure your Authorization header in a typical API request:

```http
POST https://api.paywithflash.com/v1/get_user_subscription_details
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cC...
Content-Type: application/x-www-form-urlencoded
```

***

## **Error Handling**

* If the token is **missing** or **invalid**, you will receive a `401 Unauthorized` error.
* For expired tokens, you will get a `Token has expired` message.

### **Common Authentication Errors**

* **401 Unauthorized:** Occurs when the token is invalid, missing, or expired.
* **Invalid Token Format:** Ensure that the token is sent as `Bearer <token>` and is properly formatted.

## **Sample Request With Authentication**

```bash
curl -X POST https://api.paywithflash.com/v1/get_user_subscription_details \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cC..." \
-d "email=user@example.com&flash_id=12345"
```

***

## **Important Notes:**

* Never share your Subscription API token publicly.
* Make sure to use the correct subscription token for each individual subscription.


# Endpoints

## Get user subscription details

> Retrieves subscription details and transaction history for a user

```json
{"openapi":"3.0.0","info":{"title":"NostrEggs Subscription API for Platforms","version":"1.0.0"},"servers":[{"url":"https://api.paywithflash.com/v1","description":"Production server"}],"security":[{"bearerAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"JWT token signed with the Flash secret key"}}},"paths":{"/get_user_subscription_details":{"post":{"summary":"Get user subscription details","description":"Retrieves subscription details and transaction history for a user","operationId":"getUserSubscriptionDetails","requestBody":{"required":true,"content":{"application/x-www-form-urlencoded":{"schema":{"type":"object","required":["flash_id"],"properties":{"flash_id":{"type":"string","description":"The Flash ID for the platform"},"email":{"type":"string","description":"User's email address (one of email, npub, or external_uuid required)"},"npub":{"type":"string","description":"User's Nostr public key in npub format (one of email, npub, or external_uuid required)"},"external_uuid":{"type":"string","description":"External UUID for the user (one of email, npub, or external_uuid required)"}}}}}},"responses":{"200":{"description":"Successful response","content":{"application/json":{"schema":{"type":"object","properties":{"membership":{"type":"object","properties":{"user_public_key":{"type":"string"},"is_paid":{"type":"boolean"},"membership_start_date":{"type":"string"},"next_payment_date":{"type":"string"},"status":{"type":"string"},"external_uuid":{"type":"string"}}},"transactions":{"type":"array","items":{"type":"object","properties":{"id":{"type":"integer"},"from_public_key":{"type":"string"},"to_public_key":{"type":"string"},"transaction_date":{"type":"string"},"amount_in_sats":{"type":"integer"},"amount_in_currency":{"type":"number"},"currency":{"type":"string"},"bitcoin_price":{"type":"number"},"flash_id":{"type":"string"},"flash_name":{"type":"string"},"flash_type":{"type":"string"},"to_wallet_id":{"type":"integer"},"to_user_wallet_name":{"type":"string"},"from_wallet_id":{"type":"integer"},"from_user_wallet_name":{"type":"string"},"lnurl":{"type":"string"}}}}}}}}},"400":{"description":"Bad request","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"string"}}}}}}}}}}}
```

## Cancel user subscription

> Cancels a user's active subscription

```json
{"openapi":"3.0.0","info":{"title":"NostrEggs Subscription API for Platforms","version":"1.0.0"},"servers":[{"url":"https://api.paywithflash.com/v1","description":"Production server"}],"security":[{"bearerAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"JWT token signed with the Flash secret key"}}},"paths":{"/cancel_user_subscription":{"post":{"summary":"Cancel user subscription","description":"Cancels a user's active subscription","operationId":"cancelUserSubscription","requestBody":{"required":true,"content":{"application/x-www-form-urlencoded":{"schema":{"type":"object","required":["flash_id"],"properties":{"flash_id":{"type":"string","description":"The Flash ID for the platform"},"email":{"type":"string","description":"User's email address (one of email, npub, or external_uuid required)"},"npub":{"type":"string","description":"User's Nostr public key in npub format (one of email, npub, or external_uuid required)"},"external_uuid":{"type":"string","description":"External UUID for the user (one of email, npub, or external_uuid required)"}}}}}},"responses":{"200":{"description":"Successful cancellation","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"}}}}}},"400":{"description":"Bad request","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"msg":{"type":"string"}}}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"msg":{"type":"string"}}}}}},"404":{"description":"Not found","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"msg":{"type":"string"}}}}}},"500":{"description":"Internal server error","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":"boolean"},"msg":{"type":"string"}}}}}}}}}}}
```


# Webhooks

Webhook Integration Guide for Subscription Management

This guide is intended for developers looking to integrate real-time subscription event notifications into their applications. With our webhook system, you’ll receive automated POST requests to your designated URL for events like new sign-ups, renewals, and payment failures.

## **Getting Started**

A **webhook** allows our system to send real-time updates 📡 to your application. We’ll POST a JSON payload to your specified endpoint whenever a relevant subscription event occurs.

### **Step 1: Create/Edit Subscription & Plug in Your Webhook URL**

You can set up a webhook URL in the Flash web app either while creating a new subscription or by editing an existing subscription:

1. When **creating** a new subscription:
   1. Head over to **New Subs** > **Create a Subscription Plan**
   2. Make sure to click on the “**Use Advanced Webhook Features”** checkbox to make the Webhook Url field appear
   3. Input your **Webhook URL** in the form when creating the subscription<br>

      <figure><img src="/files/d4imEWmuIxXC1NB4oPNq" alt=""><figcaption></figcaption></figure>
   4. After creating the Subscription, you will receive a url with the subscription and the subscription key. You can use this key to decode and verify the JWT token that comes with our event (see guide below).
2. When you have **already created** a subscription:
   1. Head over to My Subscriptions
   2. Select the subscription you want to add webhooks for
   3. Click on “Edit Subscription”
   4. Fill in/modify the **Webhook Url** field
   5. The subscription key is also visible in this screen and can be used to decode and verify the JWT token that comes with our event (see guide below).<br>

      <figure><img src="/files/3D2Zbny3Nz1m46YfBSAM" alt=""><figcaption></figcaption></figure>

### **Step 2: Configure Your Webhook Endpoint**

To receive webhook notifications, set up a POST endpoint in your application:

1. Ensure it accepts JSON payloads.
2. Process the data based on the event type and take appropriate actions.

**Security Tip:** Always use HTTPS to secure webhook transmissions.

#### **Responding to Webhook Calls**

Your webhook endpoint should respond with a `200 OK` status to acknowledge receipt of the event. If no response or an error is received, we may retry the notification.

***

## **Event Types**

The system triggers the following events, each sending a POST request with event data to your webhook:

<details>

<summary>1. User Signed Up (name='user_signed_up', id=1)</summary>

Triggered when a user subscribes to a plan. The payload includes details like the user's public key, plan, and transaction info.

```json
{
  "data": {
    "public_key": "55a12716a6c4e8c95fc83dc046c3ea2209d3e3a1b87b15c48ef562b5a8599ed8",
    "external_uuid": "adf8cab9-add3-400d-b83c-9b68cfb6be8d",
    "name": "Jane Doe",
    "email": "jane.doe@example.com",
    "about": "Enthusiast of digital art",
    "picture_url": "<https://example.com/jane.jpg>",
    "user_plan": "Premium",
    "user_plan_id": "23",
    "signup_date": "2024-02-15",
    "next_payment_date": "2024-03-15",
    "failed_payment_date": "",
    "transaction_id": "654",
    "transaction_amount": "100",
    "transaction_currency": "Satoshis"
    "transaction_date": "2024-02-15"
  }
}
```

</details>

<details>

<summary>2. Renewal Successful (name='renewal_successful', id=2)</summary>

Sent when a user's subscription renewal is successfully processed.

```json
{
  "data": {
    "public_key": "55a12716a6c4e8c95fc83dc046c3ea2209d3e3a1b87b15c48ef562b5a8599ed8",
    "external_uuid": "adf8cab9-add3-400d-b83c-9b68cfb6be8d",
    "name": "Jane Doe",
    "email": "jane.doe@example.com",
    "about": "Enthusiast of digital art",
    "picture_url": "<https://example.com/jane.jpg>",
    "user_plan": "Premium",
    "user_plan_id": "24"
    "signup_date": "2024-02-15",
    "next_payment_date": "2024-04-15",
    "failed_payment_date": "",
    "transaction_id": "837",
    "transaction_amount": "100",
    "transaction_currency": "Satoshis",
    "transaction_date": "2024-03-15"
  }
}
```

</details>

<details>

<summary>3. Renewal Failed (name='renewal_failed', id=3)</summary>

Sent if a subscription renewal fails, typically due to a payment issue.

```json
{
  "data": {
    "public_key": "55a12716a6c4e8c95fc83dc046c3ea2209d3e3a1b87b15c48ef562b5a8599ed8",
    "external_uuid": "adf8cab9-add3-400d-b83c-9b68cfb6be8d",
    "name": "Jane Doe",
    "email": "jane.doe@example.com",
    "about": "Enthusiast of digital art",
    "picture_url": "<https://example.com/jane.jpg>",
    "user_plan": "Premium",
    "user_plan_id": "67",
    "signup_date": "2024-02-15",
    "next_payment_date": "",
    "failed_payment_date": "2024-03-15",
    "transaction_id": "-1",
    "transaction_amount": "100",
    "transaction_currency": "Satoshis",
    "transaction_date": ""
  }
}
```

</details>

<details>

<summary>4. Subscription Paused (name='user_paused_subscription', id=4)</summary>

Sent if a subscription renewal fails, typically due to a payment issue.

```json
{
  "data": {
    "public_key": "55a12716a6c4e8c95fc83dc046c3ea2209d3e3a1b87b15c48ef562b5a8599ed8",
    "external_uuid": "adf8cab9-add3-400d-b83c-9b68cfb6be8d",
    "name": "Jane Doe",
    "email": "jane.doe@example.com",
    "about": "Enthusiast of digital art",
    "picture_url": "<https://example.com/jane.jpg>",
    "user_plan": "Premium",
    "user_plan_id": "67",
    "signup_date": "2024-02-15",
    "next_payment_date": "",
    "failed_payment_date": "2024-03-15",
    "transaction_id": "-1",
    "transaction_amount": "100",
    "transaction_currency": "Satoshis",
    "transaction_date": ""
  }
}
```

</details>

<details>

<summary>5. Subscription Cancelled (name='user_cancelled_subscription', id=5)</summary>

Sent if a subscription renewal fails, typically due to a payment issue.

```json
{
  "data": {
    "public_key": "55a12716a6c4e8c95fc83dc046c3ea2209d3e3a1b87b15c48ef562b5a8599ed8",
    "external_uuid": "adf8cab9-add3-400d-b83c-9b68cfb6be8d",
    "name": "Jane Doe",
    "email": "jane.doe@example.com",
    "about": "Enthusiast of digital art",
    "picture_url": "<https://example.com/jane.jpg>",
    "user_plan": "Premium",
    "user_plan_id": "67",
    "signup_date": "2024-02-15",
    "next_payment_date": "",
    "failed_payment_date": "2024-03-15",
    "transaction_id": "-1",
    "transaction_amount": "100",
    "transaction_currency": "Satoshis",
    "transaction_date": ""
  }
}
```

</details>

***

## Webhook Payload Structure

The data sent to your webhook URL will be structured as a JSON object containing two primary fields: **`eventType`** and **`data`**. The **`eventType`** field indicates the type of event, while the **`data`** field contains event-specific information.

<details>

<summary>Here's a breakdown of the data you can expect for each event type:</summary>

* **Common Fields Across All Event Types:**
  * **`public_key`**: The user's public key.
  * **`name`**: The user's name.
  * **`email`**: The user's email address.
  * **`about`**: A brief description of the user.
  * **`picture_url`**: The URL to the user's profile picture.
  * **`user_plan`**: The subscription plan the user has signed up for.
  * **`user_plan_id`**: The subscription plan id the user has signed up for.
* **Event-Specific Fields:**
  * **`signup_date`**: The date the user signed up (only for **`user_signed_up`**).
  * **`next_payment_date`**: The next payment date for the subscription (only for **`renewal_successful`**).
  * **`failed_payment_date`**: The date of the failed payment (only for **`renewal_failed`**).
  * **`transaction_id`**: The ID of the transaction (varies by event type).
  * **`transaction_amount`**: The payment amount of the transaction(all same)
  * **`transaction_currency`**: The currency of the transaction(all same)
  * `transaction_date`: The date of the transaction (varies by event type).

</details>

***

## **Securing Webhooks with JWT**

All webhooks include a JWT token in the `Authorization` header, allowing you to verify that the event is coming from Flash. You can decode and verify this token using the subscription key provided in the web app.

Example token:

```json
{
  "version": "1.0",
  "eventType": {
    "id": "1",
    "name": "user_signed_up"
  },
  "user_public_key": "55a12716a6c4e8c95fc83dc046c3ea2209d3e3a1b87b15c48ef562b5a8599ed8",
  "exp": "2024-05-01T08:48:38Z"
}
```

**Token Expiration:** The token is valid for 1 hour.

To verify the token, decode it using your subscription key and the `HS256` algorithm.

***

## **Handling Webhooks**

Your webhook should always respond with a `200 OK` status after processing the event. Failure to do so may cause the system to retry the notification.

***

## **Best Practices**

* **Idempotency:** Ensure that your webhook handler can process the same event more than once without causing errors or duplicate actions.
* **Logging:** Implement logging for all webhook events to make troubleshooting easier.

***

## **FAQ**

**Can I set up multiple webhook URLs?**

<details>

<summary><strong>Can I set up multiple webhook URLs?</strong></summary>

Currently, we support one webhook URL per subscription. You can handle different events with routing logic in your app.

</details>

<details>

<summary><strong>What if I stop receiving webhooks?</strong></summary>

Ensure your endpoint is correctly configured and reachable. If the issue persists, contact our support team.

</details>

***

## Conclusion

By integrating our webhook system, you can stay updated on key subscription events in real time, helping automate and optimize your processes. Feel free to contact our developer support team if you need any help.

***

## Webhook Usage - full example

Assuming your webhook URL is `https://webhook` , you can refer to the following code:

{% tabs %}
{% tab title="JavaScript (NodeJS)" %}

```javascript
const jwt = require('jsonwebtoken');

app.post('/webhook', (req, res) => {
	const data = req.body;  // Extract JSON data from the request
	
	const authHeader = req.headers.authorization;
	const token = authHeader && authHeader.split(' ')[1];
	
	if (!token) {
	    return res.status(401).json({ error: 'No token provided' });
	}

	// Verify and decode the JWT token
	const secret = 'your_secret_key';
	jwt.verify(token, secret, (err, decodedToken) => {
	    if (err) {
	        return res.status(401).json({ error: 'Invalid token' });
	    }
	
	    const { version, eventType, user_public_key } = decodedToken;
	
	    // Process the data as needed
	    console.log('User public key:', user_public_key);
	    console.log('Event Type:', eventType);
	});
	
  // Access fields from the JSON payload
  const userPublicKey = data.data.public_key;
  const userName = data.data.name;
  const userEmail = data.data.email;
  
  // Process the data...
	
  res.json({ ok: true });
});

```

{% endtab %}

{% tab title="Python (Flask)" %}

```python
from flask import request, jsonify
import jwt

@app.route('/webhook', methods=['POST'])
def webhook():
    data = request.get_json()

    auth_header = request.headers.get('Authorization')
    token = auth_header.split(" ")[1] if auth_header else None

    if not token:
        return jsonify({'error': 'No token provided'}), 401

    secret = 'your_secret_key'
    try:
        decoded_token = jwt.decode(token, secret, algorithms=['HS256'])
        version = decoded_token.get('version')
        event_type = decoded_token.get('eventType')
        user_public_key = decoded_token.get('user_public_key')

        print(f"User public key: {user_public_key}")
        print(f"Event Type: {event_type}")

    except jwt.ExpiredSignatureError:
        return jsonify({'error': 'Token has expired'}), 401
    except jwt.InvalidTokenError:
        return jsonify({'error': 'Invalid token'}), 401

    user_public_key = data.get('data').get('public_key')
    user_name = data.get('data').get('name')
    user_email = data.get('data').get('email')
    
    return jsonify({'ok': True})

```

{% endtab %}

{% tab title="Ruby (Sinatra)" %}

```ruby
require 'json'
require 'jwt'

post '/webhook' do
  data = JSON.parse(request.body.read)

  auth_header = request.env['HTTP_AUTHORIZATION']
  token = auth_header.split(' ')[1] if auth_header

  halt 401, { error: 'No token provided' }.to_json unless token

  secret = 'your_secret_key'
  begin
    decoded_token = JWT.decode(token, secret, true, algorithm: 'HS256')[0]
    user_public_key = decoded_token['user_public_key']
    event_type = decoded_token['eventType']

    puts "User public key: #{user_public_key}"
    puts "Event Type: #{event_type}"
  
  rescue JWT::ExpiredSignature
    halt 401, { error: 'Token has expired' }.to_json
  rescue JWT::DecodeError
    halt 401, { error: 'Invalid token' }.to_json
  end

  user_public_key = data['data']['public_key']
  user_name = data['data']['name']
  user_email = data['data']['email']
  
  { ok: true }.to_json
end

```

{% endtab %}

{% tab title="Java (Spring Boot)" %}

```java
import org.springframework.web.bind.annotation.*;
import org.springframework.http.ResponseEntity;
import io.jsonwebtoken.*;
import java.util.Map;

@RestController
public class WebhookController {

    @PostMapping("/webhook")
    public ResponseEntity<?> handleWebhook(@RequestBody Map<String, Object> payload, 
                                           @RequestHeader("Authorization") String authHeader) {
        
        String token = authHeader != null ? authHeader.split(" ")[1] : null;
        if (token == null) {
            return ResponseEntity.status(401).body(Map.of("error", "No token provided"));
        }

        String secret = "your_secret_key";
        try {
            Jws<Claims> decodedToken = Jwts.parser()
                                            .setSigningKey(secret)
                                            .parseClaimsJws(token);
            
            String version = (String) decodedToken.getBody().get("version");
            Map<String, Object> eventType = (Map<String, Object>) decodedToken.getBody().get("eventType");
            String userPublicKey = (String) decodedToken.getBody().get("user_public_key");

            System.out.println("User public key: " + userPublicKey);
            System.out.println("Event Type: " + eventType);

        } catch (ExpiredJwtException e) {
            return ResponseEntity.status(401).body(Map.of("error", "Token has expired"));
        } catch (JwtException e) {
            return ResponseEntity.status(401).body(Map.of("error", "Invalid token"));
        }

        Map<String, Object> data = (Map<String, Object>) payload.get("data");
        String userPublicKey = (String) data.get("public_key");
        String userName = (String) data.get("name");
        String userEmail = (String) data.get("email");

        return ResponseEntity.ok(Map.of("ok", true));
    }
}

```

{% endtab %}

{% tab title="PHP" %}

```php
require 'vendor/autoload.php'; // Include JWT library (e.g., firebase/php-jwt)

use \Firebase\JWT\JWT;

$secret = 'your_secret_key';

$data = json_decode(file_get_contents('php://input'), true);

$authHeader = $_SERVER['HTTP_AUTHORIZATION'] ?? null;
if ($authHeader) {
    $token = explode(' ', $authHeader)[1];
} else {
    http_response_code(401);
    echo json_encode(['error' => 'No token provided']);
    exit;
}

try {
    $decodedToken = JWT::decode($token, $secret, ['HS256']);
    $decodedArray = (array) $decodedToken;

    $userPublicKey = $decodedArray['user_public_key'];
    $eventType = $decodedArray['eventType'];

    echo "User public key: $userPublicKey\n";
    echo "Event Type: $eventType\n";

} catch (Exception $e) {
    http_response_code(401);
    echo json_encode(['error' => 'Invalid token']);
    exit;
}

$userPublicKey = $data['data']['public_key'] ?? null;
$userName = $data['data']['name'] ?? null;
$userEmail = $data['data']['email'] ?? null;

echo json_encode(['ok' => true]);

```

{% endtab %}
{% endtabs %}


# Contact Us

Still stuck after going through our documentation? Feel free to reach out to us for some support help.  There are 2 ways for you to get in touch with us:

* **Live chat**: use the widget in the app.
* **Support ticket**: create a ticket [here](https://aekbf2dhopq.typeform.com/to/evOdc7ZA).


