User Guide
Welcome to the ElenPAY User Guide. This guide will help you integrate and use our payment system, covering deposits, withdrawals, webhooks, and best practices for a smooth experience.
API Documentation
You can find the full API reference here: ElenPAY API
Before You Start
Note: Store setup, API authentication, and webhook registration are handled by our operations team. Merchants do not need to configure these themselves. If you need to add a new store, change your store currency, or update webhook endpoints, please contact our support or ops team.
Error Handling
For details on API error responses, error codes, and troubleshooting failed requests, please refer to the API Documentation. The API docs include example error payloads and guidance on interpreting error messages.
Currency Support
Each store is configured to accept either BTC or a single fiat currency (e.g., USD, EUR). If you wish to accept payments in multiple currencies, you must request a separate store for each currency through our ops team. You do not need to provide the team with a different callback url.
Invoice Expiry
- Lightning (Bolt11) invoices expire after 15 minutes by default.
- You can set a custom expiration by including the
checkout.expirationMinutes
field (in minutes) in your invoice creation request (see API docs for details). - Payments cannot be made after expiry; the Lightning Network enforces this restriction.
Staging Environment
- All integration and testing should be performed against our staging environment, referenced as
api-staging.elenpay.tech
in this guide. - The staging environment mirrors production but is safe for test transactions.
Deposits (Receiving Payments)
With Widget (Recommended)
A deposit is the process of receiving a payment from an end user using the ElenPAY payment widget.
Flow:
- The end user clicks "Checkout with Lightning" on your website.
- Your backend creates an invoice by calling the ElenPAY API.
- ElenPAY returns the invoice details, including a
checkoutLink
. - You display the checkout link to the user, which opens the ElenPAY payment widget.
- The user pays through the widget using their preferred Lightning wallet.
- ElenPAY notifies you of the payment status via webhook (settled or expired).
Sequence Diagram:
sequenceDiagram
EndUser->>Merchant: Click on "Checkout with Lightning"
Merchant->>ElenPAY: Create invoice
ElenPAY-->>Merchant: Return invoice details (with checkoutLink)
Merchant->>EndUser: Show checkout widget
EndUser-->>ElenPAY: Pay with Lightning wallet
ElenPAY->>Merchant: Webhook notification (settled, expired)
Redirecting to the Widget
When initiating a deposit, you can configure the redirectURL
and redirectAutomatically
fields. This will allow the widget to redirect to your website after a deposit is completed:
redirectURL
: Specifies the URL where the customer will be redirected after payment completion. Make sure your url starts withhttps://
redirectAutomatically
: If set totrue
, the widget will automatically redirect the customer to theredirectURL
after payment completion. Otherwise, we will expose a button that the user can click for going back to your website.
Without Widget (Direct Bolt11)
A deposit can also be processed without the widget, by directly presenting the user with a Bolt11 invoice.
Flow:
- The end user initiates a payment on your site.
- Your backend creates an invoice by calling the ElenPAY API.
- You obtain the Bolt11 invoice from the payment methods endpoint.
- You display the Bolt11 invoice (QR code or string) to the user for payment.
- ElenPAY notifies you of the payment status via webhook (settled or expired).
Sequence Diagram:
sequenceDiagram
EndUser->>Merchant: Initiate payment
Merchant->>ElenPAY: Create invoice
ElenPAY-->>Merchant: Return invoice details
Merchant->>ElenPAY: Get payment methods (Bolt11)
Merchant->>EndUser: Show Bolt11 invoice (QR or string)
EndUser-->>ElenPAY: Pay with Lightning wallet
ElenPAY->>Merchant: Webhook notification (settled, expired)
Creating an Invoice
Endpoint: POST /api/v1/stores/{storeId}/invoices
Description: Creates a new invoice for a deposit. Use this to request payment from a user via Lightning or the widget.
Required fields:
checkout.paymentMethods
:["BTC-LightningNetwork"]
currency
:BTC
or a fiat currency (e.g., USD, EUR). Fiat rates are always based on BTC.amount
: Use two decimals for fiat, eight for BTC (e.g.,5.00
for USD,0.00100000
for BTC).metadata
: JSON object with required fields:OrderId
(required): Unique order identifier for your systemCustomerId
(required): Unique customer identifier for your systemMerchantId
(optional): Your merchant identifier if you use multiple merchant accounts
Example metadata:
{
"OrderId": "12345678",
"CustomerId": "401329b6-dd4e-4b4e-a866-730d4bc116c4",
"MerchantId": "5GtvTUFXtJGrqGV5kqMbadtKr4aL2Z63Muk2rAQkYzyT" // Optional
}
Example CURL:
export STORE_ID="XXXXXXXXXXXXXX"
export API_TOKEN="XXXXXXXXXXXXX"
export URL="https://api-staging.elenpay.tech"
curl -X POST "${URL}/api/v1/stores/${STORE_ID}/invoices" \
-H "accept: application/json" \
-H "Content-Type: application/json" \
-H "Authorization: Token ${API_TOKEN}" \
-d '{
"metadata": {
"OrderId": "12345678",
"MerchantId": "5GtvTUFXtJGrqGV5kqMbadtKr4aL2Z63Muk2rAQkYzyT",
"CustomerId": "401329b6-dd4e-4b4e-a866-730d4bc116c4"
},
"checkout": {"paymentMethods": ["BTC-LightningNetwork"]},
"amount": "5.00",
"currency": "USD"
}'
Response:
- The response contains the invoice
id
. Store this in your database for future reference.
Payment Links
Payment links allow you to direct users to complete payments using either the ElenPAY widget or a direct Lightning invoice. Below are the two options:
1. Checkout Link (Widget)
Use the checkoutLink
field from the invoice creation response to direct users to the ElenPAY widget. This widget provides a seamless payment experience.
Example:
https://<ElenPAY_domain>/i/<invoiceId>
2. Lightning Invoice (No Widget)
If you want to use your own widget or make the user pay from your own website, retrieve the Lightning invoice using the payment methods endpoint. The payments[0].destination
field in the response contains the Bolt11 invoice, which you can display as a QR code or a clickable link for wallets.
Endpoint: GET /api/v1/stores/{storeId}/invoices/{invoiceId}/payment-methods
Example:
lnbc1...
Withdrawals (Pull Payments)
With Widget
A withdrawal (pull payment) can be processed using the ElenPAY withdrawal widget, allowing the user to choose how to receive their funds.
Endpoint: POST /api/v1/stores/{storeId}/withdrawal/request
Description: Initiates a withdrawal and returns a checkoutLink
for the user to complete the process via the widget.
Flow:
- You request a withdrawal by calling the endpoint above.
- ElenPAY returns a
checkoutLink
for the withdrawal. - You present the checkout link to the user, which opens the ElenPAY withdrawal widget.
- The user chooses how to receive the funds (e.g., by providing a Lightning invoice or using LNURL) through the widget.
- ElenPAY processes the withdrawal and notifies you via webhook.
Sequence Diagram:
sequenceDiagram
Merchant->>ElenPAY: Request withdrawal
ElenPAY-->>Merchant: Return withdrawal details (with checkoutLink)
Merchant->>EndUser: Show withdrawal widget
EndUser-->>ElenPAY: Choose payout method (e.g., Lightning invoice, LNURL)
ElenPAY->>Merchant: Webhook notification (settled, cancelled)
Widget Screenshots:
-
Type Selection:
-
LNURL Option:
-
Invoice Option:
Redirecting to your website
When initiating a withdrawal, you can configure the redirectURL
and redirectAutomatically
fields. This will allow the widget to redirect to your website after a withdrawal is completed:
redirectURL
: Specifies the URL where the customer will be redirected after withdrawal completion. Make sure your url starts withhttps://
redirectAutomatically
: If set totrue
, the widget will automatically redirect the customer to theredirectURL
after withdrawal completion. Otherwise, we will expose a button that the user can click for going back to your website.
Without Widget
A withdrawal can also be processed directly, by providing the payout details up front (e.g., a Lightning invoice).
Flow:
- You request a withdrawal by calling the appropriate endpoint for the withdrawal type (see below).
- ElenPAY processes the withdrawal asynchronously and pays the user directly.
- You receive webhook notifications about the withdrawal status (settled or cancelled).
Sequence Diagram:
sequenceDiagram
Merchant->>ElenPAY: Request withdrawal (with payout details)
ElenPAY-->>Merchant: Return withdrawal details
ElenPAY->>EndUser: Pay user directly (e.g., Lightning invoice, LNURL)
ElenPAY->>Merchant: Webhook notification (settled, cancelled)
a) Withdraw to a Lightning Invoice
Endpoint: POST /api/v1/stores/{storeId}/invoice-withdrawal
Description: Pays the given amount to a provided Bolt11 invoice. Payment is processed asynchronously and retried if it fails. To withdraw directly to a Lightning invoice, include the paymentRequest
field in your request.
Required fields:
amount
: Amount to withdraw (BTC, up to 8 decimals)currency
: Must beBTC
metadata
: JSON object with required fields:OrderId
(required): Unique order identifier for your systemCustomerId
(required): Unique customer identifier for your systemMerchantId
(optional): Your merchant identifier if you use multiple merchant accountspaymentRequest
: The Bolt11 invoice to pay
Request Example:
{
"amount": "0.001",
"currency": "BTC",
"metadata": {
"OrderId": "12345678",
"CustomerId": "401329b6-dd4e-4b4e-a866-730d4bc116c4"
},
"paymentRequest": "lnbc1..."
}
b) Withdraw via LNURL
Endpoint: POST /api/v1/stores/{storeId}/withdrawal/request
Description: Initiates a withdrawal and returns a link
(LNURL) for the user to complete the process via their wallet.
Required fields:
amount
: Amount to withdraw (BTC, up to 8 decimals)currency
: Must beBTC
metadata
: JSON object with required fields:OrderId
(required): Unique order identifier for your systemCustomerId
(required): Unique customer identifier for your systemMerchantId
(optional): Your merchant identifier if you use multiple merchant accounts
Request Example:
{
"amount": "0.001",
"currency": "BTC",
"metadata": {
"OrderId": "12345678",
"CustomerId": "401329b6-dd4e-4b4e-a866-730d4bc116c4"
},
}
How to use the LNURL:
- You will receive a
link
field in the response, which is an LNURL (bech32-encoded URL). - Display this LNURL to the user as a QR code or provide it as a string for them to paste into their Lightning wallet.
- The user scans the QR code or pastes the LNURL in their wallet. Their wallet will then negotiate the withdrawal details directly with our backend.
- Once the user completes the withdrawal in their wallet, we will process the payment and notify you via webhook when it is settled or cancelled.
Note: For security reasons, a LNURL link can be used once. If at any point the process fails, the merchant must request a different LNURL from ElenPAY
Withdrawal Retry Policy
If a withdrawal (pull payment) fails due to network, routing, or liquidity issues, our system will automatically retry the withdrawal multiple times over a period of time. You do not need to manually retry failed withdrawals; the system handles retries for you.
If all attempts fail, the withdrawal will be marked as cancelled and you will receive a PullPaymentCancelled
webhook event. After this webhook is received, it is safe to to ask for another withdrawal for the same order.
Webhooks
Webhooks notify you about important events:
InvoiceSettled
: Payment receivedInvoiceExpired
: Invoice expiredPullPaymentSettled
: Withdrawal completedPullPaymentCancelled
: Withdrawal failed/cancelled
Webhook Security
Webhooks are a critical integration point and must be handled securely to prevent spoofing or unauthorized actions. Follow these best practices:
1. Signature Verification
- Every webhook request from ElenPAY includes a
BTCPay-Sig
header. - This header contains a HMAC SHA256 signature of the raw request body, using your webhook secret as the key.
- Always verify this signature before processing the webhook. If the signature does not match, reject the request (e.g., respond with HTTP 401 Unauthorized).
2. Webhook Secret
- Your webhook secret is provided by our ops team during store setup.
- Store this secret securely and never expose it publicly or in client-side code.
- If you believe your secret has been compromised, contact support immediately to rotate it.
3. Secure Endpoint
- Your webhook endpoint must be accessible from the public internet.
- Use HTTPS to encrypt traffic and prevent interception or tampering.
- Do not expose sensitive business logic or data at the webhook endpoint.
4. Idempotency and Replay Protection
- Webhooks will be retried if your server returns an HTTP status code of 400 or higher.
- Ensure your webhook handler is idempotent (processing the same event multiple times has no adverse effect).
- Use unique event IDs (e.g.,
deliveryId
in the payload) to detect and ignore duplicate events.
5. Logging and Monitoring
- Log webhook deliveries and verification failures for auditing and troubleshooting.
- Monitor for unexpected or failed webhook attempts.
6. Example: Signature Verification
See examples.md for a C# code sample. You can implement similar logic in any backend language.
Example webhook payloads and more details: See Webhook Types below.
7. Metadata in Webhooks
For the merchant's convenience, every webhook includes the metadata provided during the initiation of the transaction. This allows you to easily match webhook events to your internal records, such as orders or customer details.
Example: If you provided
OrderId
,CustomerId
, andMerchantId
in the metadata when creating an invoice or withdrawal, these fields will be included in the webhook payload.
See the Webhook Types section for example payloads and field descriptions.
Best Practices
- Always store invoice and pull payment IDs for future reference.
- Validate webhook signatures to prevent spoofing.
- Handle API errors and rate limits gracefully (implement retries with backoff).
- Keep your API keys and webhook secrets secure.
Webhook Types
Event Name | Description |
---|---|
InvoiceExpired |
Invoice (deposit) has expired |
InvoiceSettled |
Invoice (deposit) has been settled |
PullPaymentSettled |
Pull payment (withdrawal) has been settled |
PullPaymentCancelled |
Pull payment (withdrawal) has been cancelled (e.g., Lightning errors, node offline, retry time exhausted) |
Example: Deposit Webhook
{
"manuallyMarked": false,
"currency": "BTC",
"cryptoAmount": 0.001,
"fiatAmount": 350.00,
"deliveryId": "KcaTiTbW3wizMoRy4LY2qY",
"webhookId": "Ery8f2rjP3iLzbF2pVqkuF",
"originalDeliveryId": "CPbfRrDc6RDUigbim8V4fd",
"isRedelivery": false,
"type": "InvoiceSettled",
"timestamp": 1717000000,
"storeId": "5GtvTUFXtJGrqGV5kqMbadtKr4aL2Z63Muk2rAQkYzyT",
"invoiceId": "3J2zjjKBkY3Bt2yDk91mHy",
"metadata": {
"OrderId": "12345678",
"CustomerId": "401329b6-dd4e-4b4e-a866-730d4bc116c4"
}
}
Example: Withdrawal Webhook
{
"currency": "BTC",
"cryptoAmount": 0.001,
"fiatAmount": 350.00,
"deliveryId": "Cs49yfQw5mWAJu1ZrYzQDj",
"webhookId": "S6Y7qomhBe4yCKATRFm1nN",
"originalDeliveryId": "A1DMdwVYMKf6DWgm1X9YEU",
"isRedelivery": false,
"type": "PullPaymentSettled",
"timestamp": 1717000000,
"storeId": "5GtvTUFXtJGrqGV5kqMbadtKr4aL2Z63Muk2rAQkYzyT",
"pullPaymentId": "6HUy85LvPuHCY6Gvpn8ZRP",
"invoiceId": null,
"reason": null,
"metadata": {
"OrderId": "12345678",
"CustomerId": "401329b6-dd4e-4b4e-a866-730d4bc116c4"
}
}
Troubleshooting
- If you miss a webhook, you can poll the invoice or withdrawal status via the API or redeliver it from the ElenPAY backoffice.
- Webhooks are retried automatically if your server is temporarily unavailable.
- For any questions, contact our support team.
For additional questions, see our FAQ.
This guide is designed to be clear and actionable. If you have suggestions or need more examples, let us know!