Skip to content

E-commerce checkout

Create a payment, redirect the customer to pay, receive a webhook when payment confirms, and redirect the customer to your order confirmation page.


Flow

Your server  →  POST /payments/requests  →  DropPay
Customer     →  Redirected to pay page
Customer     →  Pays with XRP wallet
DropPay      →  Redirects customer back to your redirect_url
DropPay      →  Sends payment.confirmed webhook to your server
Your server  →  Fulfills order

Step 1 - Create the payment at checkout

When the customer clicks "Pay with XRP", create a payment request server-side and redirect to pay_url.

checkout.py
import requests
from flask import Flask, redirect, session

app = Flask(__name__)
BASE = "https://droppayxrp.com"
API_KEY = "dp_live_your_api_key_here"

@app.route("/checkout", methods=["POST"])
def checkout():
    order_id = session.get("pending_order_id")
    amount = get_order_total(order_id)  # your function

    response = requests.post(
        f"{BASE}/payments/requests",
        headers={"Authorization": f"Bearer {API_KEY}"},
        json={
            "amount": amount,
            "currency": "XRP",
            "description": f"Order #{order_id}",
            "order_id": order_id,
            "redirect_url": "https://yourstore.com/payment-complete"
        }
    )

    payment = response.json()
    return redirect(BASE + payment["pay_url"])
checkout.js
import express from "express";

const BASE = "https://droppayxrp.com";
const API_KEY = "dp_live_your_api_key_here";

router.post("/checkout", async (req, res) => {
  const { orderId } = req.session;
  const amount = await getOrderTotal(orderId);

  const response = await fetch(`${BASE}/payments/requests`, {
    method: "POST",
    headers: {
      "Authorization": `Bearer ${API_KEY}`,
      "Content-Type": "application/json"
    },
    body: JSON.stringify({
      amount,
      currency: "XRP",
      description: `Order #${orderId}`,
      order_id: orderId,
      redirect_url: "https://yourstore.com/payment-complete"
    })
  });

  const payment = await response.json();
  res.redirect(BASE + payment.pay_url);
});

Step 2 - Handle the customer return

After payment, DropPay redirects the customer to your redirect_url with:

https://yourstore.com/payment-complete?request_id=dp_xxxxxxxxxx&tx_hash=ABC123...&order_id=order_1234

Show the customer a "Thank you" page. Do not fulfill the order here.

payment_return.py
from flask import request, render_template

@app.route("/payment-complete")
def payment_complete():
    order_id = request.args.get("order_id")
    return render_template("thank_you.html", order_id=order_id)
router.get("/payment-complete", (req, res) => {
  const { order_id } = req.query;
  res.render("thank-you", { orderId: order_id });
});

Warning

Anyone can visit your redirect_url with arbitrary parameters. Never use the redirect to fulfill an order.


Step 3 - Fulfill from the webhook

When DropPay confirms the transaction on-chain, it sends payment.confirmed to your webhook endpoint.

webhook.py
import hmac, hashlib, time
from flask import request, abort

WEBHOOK_SECRET = "your_webhook_secret"

@app.route("/webhook/droppay", methods=["POST"])
def droppay_webhook():
    timestamp = request.headers.get("X-DropPay-Timestamp", "")
    signature = request.headers.get("X-DropPay-Signature", "")
    body = request.get_data()

    if abs(int(time.time()) - int(timestamp)) > 300:
        abort(400)

    expected = "sha256=" + hmac.new(
        WEBHOOK_SECRET.encode(),
        timestamp.encode() + b"." + body,
        hashlib.sha256
    ).hexdigest()

    if not hmac.compare_digest(expected, signature):
        abort(400)

    event = request.json
    if event["event"] == "payment.confirmed":
        order_id = event["data"]["order_id"]
        if not already_fulfilled(order_id):
            fulfill_order(order_id)

    return "", 200
webhook.js
import crypto from "crypto";

const WEBHOOK_SECRET = "your_webhook_secret";

router.post("/webhook/droppay",
  express.raw({ type: "application/json" }),
  async (req, res) => {
    const timestamp = req.headers["x-droppay-timestamp"];
    const signature = req.headers["x-droppay-signature"];

    if (Math.abs(Date.now() / 1000 - Number(timestamp)) > 300) {
      return res.status(400).end();
    }

    const expected = "sha256=" + crypto
      .createHmac("sha256", WEBHOOK_SECRET)
      .update(timestamp + "." + req.body)
      .digest("hex");

    if (!crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(signature))) {
      return res.status(400).end();
    }

    const event = JSON.parse(req.body);
    if (event.event === "payment.confirmed") {
      const { order_id } = event.data;
      if (!await alreadyFulfilled(order_id)) {
        await fulfillOrder(order_id);
      }
    }

    res.status(200).end();
  }
);

USD pricing

If your store prices products in USD, pass currency: "USD" and let DropPay handle the XRP conversion. The rate is locked when the customer opens the pay page.

json={
    "amount": 49.99,
    "currency": "USD",
    "description": "Annual subscription",
    ...
}

The payment.confirmed webhook includes amount and currency showing what was requested, and xrp_usd_rate showing the rate at creation time.


Next steps

  • Webhooks reference - full payload, signature verification, security checklist
  • Pay-per-access - gate downloads or content behind a one-time payment
  • API payments - accept programmatic payments per API call without a browser