--- title: "Credit-Based Subscriptions - Wallets" slug: "wallets-1" description: "Learn how to integrate a credit-based subscription paywall: check wallet balances, redeem credits securely, prevent double-charging, and grant article access." updated: 2026-10-27T15:19:53Z published: 2026-10-27T15:19:53Z canonical: "help.frisbii.com/wallets-1" --- > ## Documentation Index > Fetch the complete documentation index at: https://help.frisbii.com/llms.txt > Use this file to discover all available pages before exploring further. # Credit-Based Subscriptions - Wallets ## Overview A **credit-based subscription** gives your customers a **wallet** of credits that they spend to unlock individual pieces of content — an article, an e-paper edition, a webinar. The customer buys a credit-based offer once, receives a balance of credits, and then pays for each item with credits instead of money. This guide covers the part of the integration that is **not** handled automatically for you: what your website must do when a customer clicks a paywalled article and wants to **redeem a credit** to read it. Configuring the offer and the wallet in the Merchant Backend is covered in the articles under [**Before you begin**](/frisbii-media/docs/wallets-1#before-you-begin); this guide starts where those leave off. > [!NOTE] > **Note** > > Redeeming a credit is a **server-to-server** operation. Frisbii Media does not ship a ready-made "spend credits" button in the Checkout or JS-SDK paywall. Your backend calls the wallet API, and your application decides what the customer may read. This gives you full control over the paywall experience, but the access logic described below is **your responsibility to implement**. ## How the pieces fit together | Concept | What it is | Where it is created | | --- | --- | --- | | **Credit-based offer** | The offer the customer buys (single product or subscription). Subscription types: `CREDIT_BASED`, `CREDIT_TRIGGER_BASED`, `TIME_CREDIT_BASED`. | Merchant Backend → Offers | | **Wallet** | The credit account, identified by a `uniqueId`. Holds the balance (`availableCreditCount`). | Merchant Backend → Products, linked to the offer | | **Credit** | A unit in the wallet. One purchase adds the offer's consumption units to the wallet. | Filled automatically on purchase/recharge | | **Redemption** | Spending credits from the wallet — the `POST /wallets/use` call. | **Your backend (this guide)** | | **Access** | The customer being allowed to read the item after redemption. | **Your application (this guide)** | The end-to-end flow for a single article looks like this: ```mermaid sequenceDiagram    participant C as Customer (browser)    participant P as Your paywall (JS-SDK / SSO)    participant S as Your backend    participant FM as Frisbii Media API    Note over C,FM: Setup — done earlier    C->>FM: Buy credit-based offer (Checkout)    FM-->>FM: Fill wallet (availableCreditCount up)    Note over C,FM: Runtime — redeeming a credit    C->>P: Click paywalled article    P->>FM: SSO login    FM-->>P: Authenticated session    P->>S: Request article (with SSO session)    S->>FM: GET /wallets/{customerId}/{uniqueId}    FM-->>S: availableCreditCount    alt Already unlocked        S-->>C: Serve article (no credit spent)    else Enough credits        S-->>C: Show "Read for 1 credit"        C->>S: Confirm        S->>FM: POST /wallets/use        FM-->>S: 200 OK (balance down, usage logged)        S->>S: Record unlock (or create access right)        S-->>C: Serve article    else Not enough credits        S-->>C: Offer top-up / purchase    end ``` The **setup** steps (buying the offer, filling the wallet) are covered by existing documentation. The **runtime** steps — SSO, balance check, redemption, and serving the article — are what this guide documents. --- ## Before you begin - You have created a **wallet** and a **credit-based offer**, and you know the wallet's `uniqueId`. See [Credit-Based Offers](/frisbii-media/docs/verbrauchsbasierte-angebote), [Credit-based subscription](/frisbii-media/docs/verbrauchsbasiertes-abonnement) and [Wallets](/frisbii-media/docs/wallets). - Customers can already **buy** the offer through your [Checkout](/frisbii-media/docs/checkout-1) integration. A completed purchase fills the wallet automatically. - You have a **server-side API access token** with the **Wallets** access group enabled. See [API Access Tokens](/frisbii-media/docs/api-zugangs-token). The token is sent in the `X-plenigo-token` header and must **never** be exposed in the browser. - Your paywall identifies the logged-in customer through [SSO](/frisbii-media/docs/authentication-sso) , so your backend can resolve the customer's **Frisbii Media** `customerId`. --- ## Step 1: Identify the customer through SSO Credit redemption is tied to a specific customer, so your backend needs the customer's Frisbii Media `customerId` before it can touch the wallet. 1. **Log the customer in** — Use the JS-SDK SSO or [Cookie-based SSO](/frisbii-media/docs/cookie-based-sso) on your paywall so the visitor has an authenticated Frisbii Media session. 2. **Resolve the** `customerId` — From the SSO session (or, if you run [External User Management](/frisbii-media/docs/checkout-with-external-users), from your own mapping of your user to the Frisbii Media customer), obtain the `customerId` on your server. **Do the wallet calls from your server, not the browser** The wallet endpoints are authenticated with your merchant API token. Never call them from client-side JavaScript, and never ship the token to the browser. The browser's job is only to authenticate the customer (SSO) and to trigger a request to *your* backend. ## Step 2: Read the wallet balance Before you show the "Read for 1 credit" prompt, check how many credits the customer has. Query the customer's wallet by its `uniqueId`. **Request** ```http GET /api/v3.0/wallets/{customerId}/{uniqueId} Host: api.frisbii-media.com X-plenigo-token: {your_api_access_token} ``` ```bash curl -X GET \  "https://api.frisbii-media.com/api/v3.0/wallets/CUSTOMER_ID/my-article-wallet" \  -H "X-plenigo-token: {your_api_access_token}" ``` **Response** (`200 OK`) ```json {  "customerCreditWalletId": 480123,  "customerId": "CUSTOMER_ID",  "uniqueId": "my-article-wallet",  "title": "Article credits",  "availableCreditCount": 8,  "creditValidityTimeStart": "2026-07-01T09:00:00Z" } ``` Use `availableCreditCount` to drive your paywall: - `availableCreditCount >= creditsNeeded` — show the "Read for 1 credit" prompt. - `availableCreditCount < creditsNeeded` — show a top-up / buy path (start a new [Checkout](/frisbii-media/docs/checkout-1) for the credit-based offer) instead. To display history or reconcile, you can also use `GET /api/v3.0/wallets/` (all wallets for the customer), `GET /api/v3.0/wallets/creditsUsed`, and `GET /api/v3.0/wallets/creditsUploaded`. ## Step 3: Redeem the credit When the customer confirms, your backend spends credits from the wallet with a single call. This is the core of the integration. **Request** ```http POST /api/v3.0/wallets/use Host: api.frisbii-media.com X-plenigo-token: {your_api_access_token} Content-Type: application/json ``` ```json {  "customerId": "CUSTOMER_ID",  "uniqueId": "my-article-wallet",  "creditsUsed": 1,  "reason": "article:2026-07-29-market-outlook",  "category": "article" } ``` | Field | Required | Notes | | --- | --- | --- | | `customerId` | Yes | The Frisbii Media customer ID. | | `uniqueId` | Yes | The wallet's unique ID (the credit account to debit) — **not** an article ID. | | `creditsUsed` | Yes | Number of credits to spend. Min `1`, max `100000`. Usually `1`. | | `reason` | Yes | Free text, max 100 characters. **This is the only place the redeemed item is recorded** — put your own article reference here (see the caveat below). | | `category` | No | Free text, max 100 characters. Useful for grouping/reporting (e.g. `article`, `epaper`, `webinar`). | **On success** (`200 OK`) Frisbii Media atomically: - decrements `availableCreditCount` by `creditsUsed`, - writes a **credit usage** record (visible via `GET /wallets/creditsUsed` and in the [Wallet Change History](/frisbii-media/docs/wallet-änderungshistorie)), and - if the balance reaches **0** on a subscription wallet, schedules the subscription's next billing so the wallet recharges according to its configured recharge type. **On insufficient balance** (`400 Bad Request`) the response carries the message **"Wallet does not contain enough credits."** (Frisbii Media error entity `102000`, detail code `1`). Handle it as "cannot unlock" and route the customer to a top-up / purchase. ## Step 4: Grant access and serve the article This is the step that Frisbii Media leaves to you, and the reason the redemption flow can feel "missing". **Important: redeeming a credit does not create access to a specific article** - The `/wallets/use` call debits the wallet and logs a usage record. It does **not** store which article was unlocked (only your free-text `reason`/`category`), and it does **not** create an access right for that article. - Every call to `/wallets/use` spends credits again. If the customer reloads the page or clicks twice, you will charge a second credit unless you prevent it. Your application must therefore (a) remember what the customer has unlocked and (b) enforce that on every subsequent visit. You have two ways to implement access. Choose one and apply it consistently. ### Option A — Keep the unlock record in your own system (simplest) 1. **Before redeeming**, check your own store for an existing `(customerId, articleId)` unlock. If it exists, serve the article and **skip** the `/wallets/use` call — the customer already paid. 2. **After a successful redemption**, persist the `(customerId, articleId)` unlock (and, ideally, the returned usage record) in your database. 3. **On every future request** for that article, serve it based on your stored unlock, not by calling `/wallets/use` again. This keeps all article-level logic on your side and is the most direct fit for the wallet API. ### Option B — Convert the redemption into a Frisbii Media access right (recommended if you already use access rights) If you already gate content with [Access Rights](/frisbii-media/docs/zugriffsrechte) and [Check Access](/frisbii-media/docs/check-access), turn each successful redemption into a durable access right so the same `hasAccess` check protects both money-based and credit-based content. 1. After `/wallets/use` returns `200`, create an access right for the article, using a stable `accessRightUniqueId` per item (for example, the same identifier you use elsewhere for that article): ```http POST /api/v3.0/accessRights/{customerId}/ X-plenigo-token: {your_api_access_token} ``` 2. On every future visit, validate access with the standard check — no wallet call needed: ```http GET /api/v3.0/accessRights/{customerId}/hasAccess?accessRightUniqueIds=article:2026-07-29-market-outlook ``` ```json { "accessGranted": true } ``` **Tip: make redemption idempotent** With either option, check "does the customer already have access to this item?" *before* calling `/wallets/use`. That single guard prevents double-charging on reloads, retries and double-clicks — the most common integration bug with credits. ## Endpoint reference Base URL (production): `https://api.frisbii-media.com/api/v3.0`. Confirm the host for your environment in [API versions](/frisbii-media/docs/api-versionen). Authentication: `X-plenigo-token` header, merchant API access token with the **Wallets** access group (and **Access Rights** for Option B). | Operation | Method & path | Purpose | | --- | --- | --- | | Read a wallet | `GET /wallets/{customerId}/{uniqueId}` | Get `availableCreditCount` for one wallet. | | List wallets | `GET /wallets/` | All wallets (paginated). | | **Redeem credits** | `POST /wallets/use` | Spend credits from a wallet. | | Credit usage history | `GET /wallets/creditsUsed` | Audit spent credits. | | Credit upload history | `GET /wallets/creditsUploaded` | Audit added credits. | | Add credits manually | `POST /wallets/upload` | Top up a wallet outside a purchase. | | Refill wallets | `POST /wallets/refillWallets/{uniqueId}` | Trigger the configured recharge. | | Create access right | `POST /accessRights/{customerId}/` | (Option B) Grant durable article access. | | Check access | `GET /accessRights/{customerId}/hasAccess` | (Option B) Validate access on later visits. | ## Troubleshooting | Symptom | Likely cause | | --- | --- | | `400` "Wallet does not contain enough credits." | `availableCreditCount` is below `creditsUsed`. Read the balance first and route to a top-up. | | `404` on the wallet | Wrong `uniqueId`, wrong `customerId`, or the customer never bought a credit-based offer (no wallet yet). | | Customer charged twice for one article | No idempotency guard — you called `/wallets/use` again on reload. Add the "already has access?" check from Step 4. | | `401`/`403` | Token missing the **Wallets** access group, wrong environment token, or the token was sent from the browser. | ## Related - [Credit-based subscription](/verbrauchsbasiertes-abonnement.md) - [Credit-Based Offers](/verbrauchsbasierte-angebote.md) - [Create wallets](/was-sind-wallets-und-wie-werden-sie-angelegt.md) - [Access Rights: Check Access](/check-access.md) - [Authentication & SSO](/authentication-sso.md) - [Checkout - Prepare Purchase](/checkout-prepare-purchase.md)