> For the complete documentation index, see [llms.txt](https://pingpay.gitbook.io/docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://pingpay.gitbook.io/docs/pingpay-api/subscriptions/integration-guide.md).

# Integration Guide

Step-by-step guide to integrating Ping Subscriptions into your application.

### 1. Get Your API Key

Sign up at [pay.pingpay.io](https://pay.pingpay.io/dashboard) and grab your API key from the dashboard. Your key starts with `pk_live_` or `pk_test_`.

### 2. Set Up Your NEAR Wallet

Make sure your merchant NEAR address has interacted with `intents.near`. See NEAR Intents Setup for details.

### 3. Create a Subscription Session

When a user clicks "Subscribe" in your app, create a session server-side:

#### Node.js / TypeScript

```typescript
const response = await fetch('https://pay.pingpay.io/api/rpc/subscriptions/sessions', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'x-api-key': process.env.PING_API_KEY,
  },
  body: JSON.stringify({
    name: 'Pro Plan',
    amount: '5000000', // 5 USDC
    asset: { chain: 'near', symbol: 'USDC' },
    interval: 'MONTHLY',
    totalPeriods: 12,
    successUrl: `https://yourapp.com/subscribed?session={SESSION_ID}`,
    cancelUrl: 'https://yourapp.com/pricing',
    metadata: {
      userId: 'user_123',
      planId: 'pro',
    },
  }),
});

const session = await response.json();
// Redirect user to session.sessionUrl
```

#### Python

```python
import requests

response = requests.post(
    'https://pay.pingpay.io/api/rpc/subscriptions/sessions',
    headers={
        'Content-Type': 'application/json',
        'x-api-key': PING_API_KEY,
    },
    json={
        'name': 'Pro Plan',
        'amount': '5000000',
        'asset': {'chain': 'near', 'symbol': 'USDC'},
        'interval': 'MONTHLY',
        'totalPeriods': 12,
        'successUrl': 'https://yourapp.com/subscribed',
        'cancelUrl': 'https://yourapp.com/pricing',
    },
)

session = response.json()
# Redirect user to session['sessionUrl']
```

#### cURL

```bash
curl -s -X POST https://pay.pingpay.io/api/rpc/subscriptions/sessions \
  -H "Content-Type: application/json" \
  -H "x-api-key: pk_live_abc123" \
  -d '{
    "name": "Pro Plan",
    "amount": "5000000",
    "asset": { "chain": "near", "symbol": "USDC" },
    "interval": "MONTHLY",
    "totalPeriods": 12,
    "successUrl": "https://yourapp.com/subscribed",
    "cancelUrl": "https://yourapp.com/pricing"
  }' | python3 -m json.tool
```

### 4. Redirect to Checkout

Send the user to the `sessionUrl` returned from the create call. This opens the Ping subscription UI where they'll:

1. Connect their NEAR wallet
2. Review the plan details
3. Sign the payment intents
4. Get redirected to your `successUrl`

```typescript
// Express.js example
app.post('/api/subscribe', async (req, res) => {
  const session = await createSubscriptionSession(req.body.plan);
  res.redirect(session.sessionUrl);
});
```

### 5. Handle the Success Redirect

When the subscriber completes activation, they're redirected to your `successUrl`. The redirect includes the `subscriptionId` as a query parameter:

```
https://yourapp.com/subscribed?subscriptionId=sub_x7y8z9
```

Fetch the subscription to confirm it's active:

```typescript
app.get('/subscribed', async (req, res) => {
  const { subscriptionId } = req.query;

  const sub = await fetch(
    `https://pay.pingpay.io/api/rpc/subscriptions/${subscriptionId}`
  ).then(r => r.json());

  if (sub.status === 'ACTIVE') {
    // Grant access, update your database, etc.
    await db.users.update(sub.subscriber, { plan: 'pro', subscriptionId });
  }

  res.render('subscribed', { subscription: sub });
});
```

### 6. Listen for Webhooks

Set up a webhook endpoint to receive real-time subscription events:

```typescript
app.post('/webhooks/ping', express.raw({ type: 'application/json' }), (req, res) => {
  // Verify signature (see Webhooks docs for verifyWebhook implementation)
  const rawBody = req.body.toString();
  const timestamp = req.headers['x-ping-timestamp'];
  const signature = req.headers['x-ping-signature'];

  if (!verifyWebhook(rawBody, timestamp, signature, WEBHOOK_SECRET)) {
    return res.status(401).send('Invalid signature');
  }

  const { type, data } = JSON.parse(rawBody);

  switch (type) {
    case 'subscription.payment_released':
      console.log(`Payment ${data.releasedCount}/${data.totalPeriods} released for ${data.subscriptionId}`);
      // Update your records
      break;

    case 'subscription.payment_failed':
      console.log(`Payment failed for ${data.subscriptionId}: ${data.error}`);
      // Notify subscriber to top up balance
      break;

    case 'subscription.cancelled':
      console.log(`Subscription ${data.subscriptionId} cancelled`);
      // Revoke access
      break;

    case 'subscription.completed':
      console.log(`Subscription ${data.subscriptionId} completed all payments`);
      // Prompt for renewal
      break;
  }

  res.status(200).send('ok');
});
```

### 7. Let Subscribers Manage Their Subscription

Ping provides a hosted management page where subscribers can view their subscription details and cancel if needed:

```
https://pay.pingpay.io/subscribe/manage/{subscriptionId}
```

Link to this from your app's account settings.

### 8. Cancel from Your Backend

To cancel a subscription from your server (e.g. when a user downgrades):

```bash
curl -s -X POST https://pay.pingpay.io/api/rpc/subscriptions/sub_x7y8z9/cancel \
  -H "Content-Type: application/json" \
  -H "x-api-key: pk_live_abc123" \
  | python3 -m json.tool
```

***

### Interval Reference

| Interval | `interval` value      | Seconds                 |
| -------- | --------------------- | ----------------------- |
| Weekly   | `"WEEKLY"`            | 604,800                 |
| Monthly  | `"MONTHLY"`           | 2,592,000 (\~30 days)   |
| Yearly   | `"YEARLY"`            | 31,536,000 (\~365 days) |
| Custom   | `{ "customSecs": N }` | N (min 60)              |

### Amount Reference

Amounts are always strings in the **smallest unit** of the asset:

| Human Amount | `amount` value | Asset             |
| ------------ | -------------- | ----------------- |
| 1 USDC       | `"1000000"`    | USDC (6 decimals) |
| 5 USDC       | `"5000000"`    | USDC              |
| 0.01 USDC    | `"10000"`      | USDC              |
| 100 USDC     | `"100000000"`  | USDC              |

### Testing Tips

* Use `{ "customSecs": 120 }` for 2-minute intervals to test the full lifecycle quickly
* Use small amounts like `"10000"` (0.01 USDC) for testing
* Check the Ping dashboard at [pay.pingpay.io/dashboard/subscriptions](https://pay.pingpay.io/dashboard/subscriptions) to monitor subscription state
* Watch your webhook endpoint logs for real-time events

***

### What's Next

> **In Progress:**
>
> * SDK/client library for Node.js, Python, and more
> * Subscription upgrade/downgrade (change amount or interval mid-subscription)
> * Multi-chain support beyond NEAR
> * Subscription renewal (auto-create new subscription when current one completes)
> * Self-service webhook URL configuration in the dashboard
