Skip to content
Micup API

Shop

The shop sells six kinds of product through the Payriff payment page, charged in Azerbaijani manat (AZN): owned cosmetic items, bundles of such items, premium packages of 1, 3 or 12 months, Visionary, a boost slot for 30 days, and username cards, which buy a username of 1, 2 or 3 characters. Prices are set in US cents and converted at a rate the admin sets. The server computes every amount, and a client sends only the product. The Admin API sets the prices, campaigns and sales switches.

Every route on this page is user-only, except Get code preview the two Payriff callback routes and Receive Paddle webhook, which take no credential. The catalog, quote and order routes answer only on a self-hosted instance in the mirror premium mode. Any other instance returns 403 FEATURE_NOT_AVAILABLE_SELF_HOSTED. The premium subscription routes check neither the premium mode nor the sales switches, so a subscription Paddle still charges can always be canceled.

A product is named by its SKU.

ValueDescription
cosmetic:<item_id>An owned-availability cosmetic item, by its ID
bundle:<bundle_id>A bundle of 2-10 owned-availability items, by its ID
premium:1, premium:3, premium:12Premium time of 1, 3 or 12 months
visionaryLifetime premium with a Visionary number
boost:30One boost slot for 30 days
username:1, username:2, username:3A username card for a username of 1, 2 or 3 characters

An item or bundle ID is a positive integer within the signed 64-bit range. A value of any other shape fails validation with 400 INVALID_FORM_BODY.

The server computes a price in one place, in whole numbers:

  1. The campaign discount comes off the list price while the campaign has not ended by the server clock.
  2. The premium member discount comes off that, for cosmetic items and bundles only, and only for an account with paid premium. An account in its free trial gets no member discount. Lifetime premium counts as paid.
  3. The US dollar price is converted to qəpik, hundredths of a manat, at the admin rate.

Each step rounds half up. A list price of 500 cents with a 20% campaign and a 30% member discount comes to 400 cents, then 280 cents ($2.80), which is 476 qəpik (4.76 AZN) at a rate of 1.7000. A product whose final price would fall below 1 cent or below 0.10 AZN is not sold.

Premium packages, Visionary and username cards take campaigns, and never take the member discount.

Each API process keeps the catalog for up to 60 seconds, and the quote reads a price list kept for up to 5 seconds, so both are for display. Create shop order reads the stored price and settings directly, and the amount it records is the amount charged.

FieldTypeDescription
base_usd_centsintegerThe list price in US cents
campaign_percent?integerThe campaign discount, null without a running campaign
campaign_ends_at?ISO8601 timestampWhen the campaign ends, null without a campaign or without an end
sale_usd_centsintegerThe price after the campaign, equal to base_usd_cents without one
premium_usd_cents1?integerWhat an account with paid premium pays for an item or a bundle
payable??payable amount objectWhat this account pays now, in its provider’s currency; null when nobody sells to it or it cannot be charged
list_payable??payable amount objectThe list price without a campaign or the member discount, in that currency
plus_payable??payable amount objectWith the member discount, for an item or a bundle; null when the account gets it already or there is none

1 Null while the member discount is 0, and always null for premium packages and Visionary

Where the admin sets a lira or manat list price, a route charging that currency takes it, less the same campaign and member discount percentages, instead of the converted amount.

FieldTypeDescription
amount_minorintegerThe amount in the minor unit of currency: cents, kuruş or qəpik
currencystringAZN, USD or TRY
FieldTypeDescription
premium_sales_openbooleanWhether premium packages and Visionary are on sale
item_sales_openbooleanWhether cosmetic items and bundles are on sale
premium_discount_percentintegerThe member discount on items and bundles for accounts with paid premium (0-95)
usd_to_azn_ratestringManat per US dollar with four decimal places, such as 1.7000, for display
usd_to_azn_rate_micro??integerExact AZN per USD in millionths; use this for catalog AZN display instead of the rounded legacy string
try_to_azn_rate_micro??integerEffective AZN per TRY in millionths; null means show AZN alone
try_rate_date??dateAutomatic CBAR bulletin date in YYYY-MM-DD format; null before the first accepted bulletin
try_rate_expires_at??ISO8601 timestampWhen an automatic display rate becomes stale; null for a manual override or no effective rate

Both sales fields read false while Payriff is not configured.

Clients display the informational TL price and the payable AZN total. Convert the selected catalog US-cent price to qepik using the exact integer rate and the server’s half-up rule, then divide that AZN amount by the effective TRY rate for the TL estimate. Quotes and orders provide the authoritative amount_azn_minor; a TRY rate must never be used to create or price an order. Existing dollar fields remain for older clients.

The effective TRY rate uses the admin override when set. Otherwise it uses the automatic CBAR rate only while the bulletin is at most 10 calendar days old in Asia/Baku. Clients stop displaying that rate at try_rate_expires_at, even if the page stays open or a catalog refresh fails. No rate or a stale automatic bulletin gives null without closing sales. CBAR is refreshed by a worker; catalog requests do not contact CBAR. Bank conversion rates and foreign transaction fees can make the card statement’s TL total differ from this estimate.

FieldTypeDescription
itemcosmetic item objectThe item
price1?shop price objectThe price of an owned item

1 Null for a premium-availability item, which comes with premium and is not sold

FieldTypeDescription
recurring_quote??recurring quote objectEach period’s subscription charge; null when no subscription is sold to the account or it cannot be charged
skustringThe product: premium:1, premium:3 or premium:12
monthsintegerThe premium time the package adds: 1, 3 or 12 months
priceshop price objectThe price
FieldTypeDescription
skustringThe product: bundle:<id>
idsnowflakeThe ID of the bundle
namestringThe display name of the bundle
description?stringA short description of the bundle
items1array[cosmetic item object]The items the bundle gives, in bundle order
priceshop price objectThe price of the bundle, with any running campaign

1 Embedded whole, because an item sold only in bundles has no price of its own and is not in the catalog items

FieldTypeDescription
skustringAlways visionary
priceshop price objectThe price, with any running campaign
totalintegerThe number of Visionary numbers the admin set
remaining1integerThe numbers with no owner and no live hold
visiblebooleanWhether Visionary is on sale: premium sales open, Visionary switched on, and Payriff configured

1 For display only. Every API process reads one count shared for 60 seconds and keeps its catalog for up to 60 seconds more, so the value can be up to 2 minutes old. Create shop order decides whether a number is free

FieldTypeDescription
skustringAlways boost:30
daysintegerHow long a bought slot lasts from payment, always 30
priceshop price objectThe price, with any running campaign, and never a member discount
FieldTypeDescription
skustringThe product: username:1, username:2 or username:3
lengthintegerThe number of characters in the username the card buys: 1, 2 or 3
priceshop price objectThe price, with any running campaign. premium_usd_cents is always null
asset1?stringThe hash of the card image, null until the admin uploads one
visiblebooleanWhether the card is on sale, as Username cards describes

1 The media proxy serves the image at /branding/cosmetic-username_card-{length}/{asset}.webp on the instance’s media endpoint, with size between 128 and 480 and animated=true for the moving image

FieldTypeDescription
usernamestringThe username checked, in lowercase
statusstringThe username card availability status
ValueDescription
availableThe username is free, and an order can hold it
ownedThe account already has the username
takenAnother account has the username, or it is reserved or in its release embargo
heldAnother account’s order holds the username, or the account’s own order does after its first 15 minutes or once paid1
held_by_youAn open order of the account holds the username, in the 15 minutes after the order opened1
not_allowedThe dictionary or the username rules refuse the username
closedNo card of the username’s length is on sale, or the username policy cannot be read
invalidThe username is not 1-3 Latin letters or digits

1 An order of the account whose payment Payriff declined or cancelled holds the username as held_by_you until its hold ends, and a new order takes the hold over.

FieldTypeDescription
premium_discountbooleanWhether the account pays premium_usd_cents for items, which needs paid premium rather than a trial
recurring_eligibility?recurring eligibility objectWhether the account can take a premium subscription now
FieldTypeDescription
settingsshop settings objectThe sales switches, the member discount and the rate
itemsarray[shop catalog item object]The priced owned items and the premium items on sale, in catalog order
packagesarray[shop package object]The priced premium packages, in 1, 3 and 12 month order
visionary?shop Visionary objectVisionary, null until the admin sets its price
boost?shop boost objectThe boost slot, null without a price, or while the caller’s provider does not sell it (Boost slots)
bundlesarray[shop bundle object]The priced bundles whose items are all on sale, in ID order
username_cards?array[shop username card object]The priced username cards in length order, empty while the username policy cannot be read
server_timeISO8601 timestampThe server clock, for campaign countdowns
viewershop viewer objectWhat applies to the requesting account

Only viewer differs between accounts. The rest is the same for every account.

FieldTypeDescription
skustringThe product
billing_mode?stringone_time or recurring, as asked
interval_months??integerRecurring only: the period, 1, 3 or 12 months; null for one_time
terms_version??stringRecurring only: the version of the subscription terms; null for one_time
base_usd_centsintegerThe list price in US cents
campaign_percentintegerThe campaign discount applied, 0 without one
premium_percentintegerThe member discount applied, 0 when it does not apply
final_usd_centsintegerThe US dollar price after both discounts
amount_azn_minorintegerThe price in qəpik: what Payriff would charge, and the AZN equivalent of the US dollar price for Paddle
usd_to_azn_ratestringThe rate used, with four decimal places
usd_to_azn_rate_micro??integerExact rate used, in millionths; use it for quote subtotal display rather than a cached catalog rate
provider?stringWho would take the payment, payriff or paddle; absent means Payriff
charge_amount_minor?integerWhat the provider would charge, in the minor unit of currency; absent means amount_azn_minor
currencystringThe currency the provider charges: AZN for Payriff, USD or TRY for Paddle
list_payable??payable amount objectThe list price without a campaign or the member discount, in the currency the provider charges
ValueDescription
pendingThe order is open and waiting for payment
paidPayriff confirmed the payment, and delivery has not finished
grantedThe item, the bundle’s items, the premium time or the Visionary number was delivered
failedNothing is delivered, and failure_reason says why
expiredThe order was still unpaid at its last check, and a payment Payriff confirms later still completes it
refundedAn admin marked a paid username card order refunded. It is final, and no check or delivery changes it
ValueDescription
amount_mismatchPayriff reported a payment whose amount or currency differs from the order
account_deletedThe account was deleted before delivery
item_deletedThe item of a cosmetic order was deleted before delivery
provider_errorPayriff did not open the order
refundedA Paddle refund or chargeback was approved before the payment was taken in, so nothing is delivered
username_takenAt delivery, the username of a username card order was taken, reserved, embargoed or held
username_not_allowedAt delivery, the dictionary or the username rules refused the username of a username card order
username_already_ownedAt delivery, the account already had the username, and not from this order
username_delivery_stalledA day after the order opened and an hour after payment, the username store still could not deliver the username

Micup sends no refund through Payriff. An admin refunds a failed payment by hand in the Payriff panel. A paid username card order that fails waits in the admin’s refund list until the admin marks it refunded.

FieldTypeDescription
idsnowflakeThe ID of the order
skustringThe product
statusstringThe shop order status
failure_reason?stringThe failure reason, null unless the order failed
billing_mode?stringone_time, or recurring for the first payment of a premium subscription
interval_months??integerRecurring only: the period, 1, 3 or 12 months
terms_version??stringRecurring only: the subscription terms the buyer accepted
base_usd_centsintegerThe list price in US cents when the order opened
campaign_percentintegerThe campaign discount applied, 0 without one
premium_percentintegerThe member discount applied, 0 when it does not apply
final_usd_centsintegerThe US dollar price charged
amount_azn_minorintegerThe amount charged, in qəpik
usd_to_azn_ratestringThe rate used, with four decimal places
usd_to_azn_rate_micro??integerExact rate stored with this order, in millionths
currencystringThe currency the provider charges: AZN for Payriff, USD or TRY for Paddle
display_currency??stringCurrency of the price shown in the shop when the order opened: TRY or AZN; null on legacy orders
display_amount_minor??integerThat shown price in minor units (kuruş or qəpik), frozen on the order; null on legacy orders
display_rate_micro??integerFrozen AZN per TRY in millionths used for the shown TRY price; null for AZN or legacy orders
provider?stringWho takes the payment, payriff or paddle; absent means Payriff
charge_amount_minor?integerWhat the provider charges, in the minor unit of currency; absent means amount_azn_minor
payment_url?stringThe payment page, null unless pending; a Paddle one ends in #t=, the public client-side token, and &l=, the buyer’s language as Paddle names it (en for a language Paddle’s checkout lacks)
paddle_checkout?1objectWhat Paddle.js needs to open the checkout; absent means use payment_url
created_atISO8601 timestampWhen the order opened
item_ids?array[snowflake]Bundle orders only: the items the order delivers, frozen when it opened, in bundle order
username??stringUsername card orders only: the username the order buys, null once the account is deleted

1 Present on a pending Paddle order that has a client token, while the deployment sets FLUXER_PADDLE_IN_APP_CHECKOUT (the app’s domain approved by Paddle). It holds transaction_id, the Paddle transaction to open, client_token, the public Paddle.js client-side token, and environment, sandbox or production

{
"id": "1501314428688998182",
"sku": "premium:3",
"status": "pending",
"failure_reason": null,
"base_usd_cents": 1200,
"campaign_percent": 25,
"premium_percent": 0,
"final_usd_cents": 900,
"amount_azn_minor": 1530,
"usd_to_azn_rate": "1.7000",
"currency": "AZN",
"payment_url": "https://payment.example.com/pay/abc123",
"created_at": "2026-09-28T20:15:00.000Z"
}
FieldTypeDescription
codestringThe code
redeemedbooleanWhether the code has been redeemed
grantshop code grant objectWhat redeeming the code gives
FieldTypeDescription
typestringpremium for premium time, or cosmetic for an item
duration_type?stringPremium only: the gift duration unit
duration_quantity?integerPremium only: the number of units
item?cosmetic item objectCosmetic only: the item

The quote and order routes refuse a product with these codes:

  • 403 SHOP_SALES_CLOSED when sales of the product’s kind are closed or Payriff is not configured. Premium packages, Visionary and the boost slot follow the premium sales switch, and cosmetic items, bundles and username cards follow the item sales switch. The boost slot is closed through Paddle while the instance has no Paddle boost product, and through Payriff while the account’s feature_shop_boost_payriff limit is 0. Username cards sell only through Payriff.
  • 404 SHOP_ITEM_UNAVAILABLE when the product has no price or no longer exists, when an item, or any item of a bundle, is not an owned-availability item on sale, when the final price would fall below 1 cent or 0.10 AZN, when an account with lifetime premium asks for a package or Visionary, or when Visionary is switched off. An order also receives it when no Visionary number is free. A username card also receives it while it is not on sale, as Username cards describes.
  • 409 COSMETIC_ALREADY_OWNED when the account already owns the item, or any item of a bundle. A bundle is never sold in part.
  • 400 MAX_PURCHASED_BOOST_SLOTS, on an order only, when the account’s live bought boost slots and its open boost orders already reach its max_purchased_boost_slots limit.
  • 409 SHOP_TOO_MANY_OPEN_ORDERS, on an order only, when the account already has three open orders. An open order is unpaid, less than 15 minutes old, and not declined, cancelled or expired at Payriff.
  • 409 SHOP_RECURRING_NOT_ELIGIBLE when the account cannot take a premium subscription now, with a recurring eligibility object as recurring_eligibility. A one-time premium order receives it too while a subscription of the account runs or its first payment is being delivered. An unpaid recurring checkout does not block a one-time premium order: the server cancels its Paddle transaction first, so only one premium checkout can ever be paid.
  • 409 SHOP_RECURRING_QUOTE_CHANGED, on a recurring order only, when recurring_consent differs from the current recurring quote. Paddle is never asked.
  • 503 SHOP_BUSY, on an order only, when the shared Payriff request budget is spent, when Payriff is backing off after a 429 or did not open the order, while another order of the same account is being opened, or when the username store is too busy to hold a username card’s username. Retrying after a few seconds is safe.
  1. Create shop order returns an order with payment_url, the Payriff payment page, which a client opens in the browser. Payriff shows the page in English and charges manat.
  2. Payriff calls Receive Payriff callback when the payment changes, and the browser comes back through Return from Payriff. Neither is trusted. A matching callback token only queues a check, in which the server reads the order from Payriff.
  3. The check accepts a payment only when Payriff reports it APPROVED or PAID for a purchase, in AZN, for exactly the amount of the order. The order becomes paid, is delivered, and becomes granted.
  4. A client follows the order with Get shop order, which reads only the server’s record and never reaches Payriff.

Apart from callbacks, the server checks every unpaid order with Payriff 2 minutes, 15 minutes, 3 hours and 24 hours after it opened. An order still unpaid at the fourth check expires, and so does an order Payriff reports expired. An order that has no Payriff order ID 10 minutes after it opened fails with provider_error.

Scheduled checks leave an expired order alone, so only a callback or a return finds a payment made after the order expired. The check that finds it makes the order paid and schedules one more check 2 minutes later, so the delivery still completes when the callback check gives up. A paid order whose delivery has to wait keeps its scheduled check until the delivery finishes.

Delivery depends on the product:

  • A cosmetic item is added to the account and worn in the slot of its type. Deliveries of one item to one account run one at a time, and a delivery that waits more than about 5 seconds is tried again later. That sends User Update, and for an avatar decoration, ultra decoration or nameplate also Guild Member Update, as Wear or take off cosmetics does.
  • A bundle delivers each item in the order’s item_ids as its own delivery, the way a cosmetic item is delivered, and wears only the first item of each type in that list. The other items are added without being worn. A delivery that stops part way is tried again later and gives only the items still missing.
  • A premium package adds its months to the account’s premium and sends User Update.
  • Visionary is delivered as Visionary describes.
  • A boost slot is delivered as Boost slots describes.
  • A username card gives the account its username, as Username cards describes.

A payment for an item the account already owns when the delivery runs grants no second copy, and neither does a second paid order for the same item delivered at the same time. The order becomes granted, and an admin refunds the payment by hand.

A paid cosmetic order whose item was deleted before delivery fails with item_deleted, and an admin refunds it by hand too.

A bundle order skips an item deleted before delivery and an item the account already owns, delivers the rest, and becomes granted. An admin refunds it by hand, as Bundles in the Admin API describes.

An order is a financial record. Deleting the account keeps its orders, which hold the account ID, the product, the items of a bundle order, the amounts, the rate, the Payriff order ID and the times, and no email address, name or IP address. A username card order also holds the username it buys until the account is deleted, which clears it. The data harvest lists every order of the account, newest first, without the payment page, whether or not Payriff is configured.

Visionary is lifetime premium with a number. The admin sets the price, the number of numbers, and whether Visionary is on sale. The catalog carries a shop Visionary object once the price is set. It stays in the catalog when every number is taken, with remaining at 0.

The visionary product takes campaigns, and never takes the member discount. It follows the premium sales switch, so closed premium sales return 403 SHOP_SALES_CLOSED. A switched-off Visionary, a missing price, or an account that already has lifetime premium, whether as a Visionary or not, returns 404 SHOP_ITEM_UNAVAILABLE.

Numbers are handed out as follows:

  • Create shop order holds the lowest free number for 15 minutes before it asks Payriff. When no number is free, it returns 404 SHOP_ITEM_UNAVAILABLE and Payriff is never called.
  • An order that fails or expires unpaid frees its number.
  • A payment that arrives after the hold ended still gets a number: the held one if it is still free, otherwise the next free one, otherwise one above the total. The Admin API counts a number above the total in over_cap_grants.
  • An account that is already a Visionary when its order is delivered gets no second number. The order still becomes granted, the number it held is freed because it was never any account’s number, and an admin refunds the payment by hand. Two orders of one account delivered together give one number.
  • A number is never given twice. A number an admin takes back, or one whose account is deleted, stays retired and is never sold again. An order whose number an admin took back never gives it again, even when its delivery is retried.

Delivery gives the account lifetime premium, with the number as its premium lifetime sequence, and the reward items the admin chose, each added and worn. It runs under the account’s premium lock, as Make user a Visionary does. While another premium change holds that lock, the delivery is tried again later. The account’s sessions receive User Update. The shop joins no Visionary guild.

In the mirror premium mode, only an account with lifetime premium can take the discriminator 0000. An account that already wears 0000 keeps it until it changes its username, and must then pick another discriminator. Otherwise the change fails with 400 INVALID_FORM_BODY and the validation code VISIONARY_REQUIRED_FOR_DISCRIMINATOR.

The boost:30 product gives the account one boost slot for 30 days from payment. Any account can buy it, with or without premium, lifetime premium included. It takes campaigns, and never takes the member discount. An account holds at most max_purchased_boost_slots live bought slots, 20 in the stock configuration.

  • Delivery adds the slot shop:<order_id> with its expires_at. A second delivery of the order adds nothing.
  • The slot boosts like a premium slot, with the same 7 day wait before it moves to another guild. It is listed in List current user boost slots until it ends, and premium ending leaves it.
  • When the slot ends, the hourly boost check takes its boost off the guild.
  • It sells through Paddle while the instance has a Paddle boost product. A full Paddle refund, or any chargeback, ends the slot at once and takes its boost off. A boost that races the refund fails with 409 CONFLICT. A partial refund leaves the slot; an admin decides by hand.
  • It sells through Payriff to an account whose feature_shop_boost_payriff limit is 1, 0 in the stock configuration, so a trait rule can open it to chosen accounts first. Payriff reports no refund: an admin refunds the payment in the Payriff panel and marks the order with Mark Payriff boost order refunded, which ends the slot and takes its boost off the same way.

A username card buys the account a username of 1, 2 or 3 characters. It is a product, username:<length>, and not a cosmetic item. The catalog lists each priced card as a shop username card object. A card is on sale only while all of these hold:

  • Item sales are open and the buyer’s payment route is Payriff, with Payriff configured and taking orders. Otherwise the quote and the order return 403 SHOP_SALES_CLOSED, and the catalog shows the card with visible false.
  • The admin switched the card’s length on, and the card has a price.
  • The username policy tier of the length is for_sale, and the username migration is selecting. A public tier takes the card out of the shop.

Otherwise the quote and the order return 404 SHOP_ITEM_UNAVAILABLE. A card takes campaigns and never takes the member discount, and an account with lifetime premium can buy one.

The username is 1-3 lowercase Latin letters or digits, exactly as long as the card. The server checks it against the dictionary and the username rules when the order opens and again at delivery. Check username card availability tells a client what an order would meet.

  • Create shop order holds the username for the account until 30 minutes after the order opened, before it asks Payriff: the order’s 15 minutes, then 15 more for a payment made in time but confirmed late. Only that order can take the username in those last 15 minutes, and any new order gets 409 USERNAME_ON_HOLD, the account’s own included, unless Payriff declined or cancelled that order’s payment. The same request again within 15 minutes returns the open order at the price it opened with, even when the card’s price or the exchange rate has changed since, and the hold does not grow.
  • An account has one open username card order at a time. An order for another username or card returns 409 SHOP_USERNAME_ORDER_OPEN while it is open, and so does the same request while Payriff has not opened the order yet, and a new order for a username the account’s own paid order holds until its delivery.
  • An account opens at most 5 username card orders a day, counted from the first that Payriff opened, and the sixth returns 429 RATE_LIMITED with retry_after set to the seconds left of that day.
  • An order that expired unpaid keeps its account from the same username until 75 minutes after it opened, with 429 RATE_LIMITED and retry_after set to the seconds left. A payment Payriff declined or cancelled has no wait, and the new order takes over the hold. A new order that then fails, at Payriff or before it, gives the hold back, and the hold ends at its own time.
  • A username another account has, or one reserved or in its release embargo, returns 409 USERNAME_TAKEN. One held by another account’s order returns 409 USERNAME_ON_HOLD, and the account’s own username 409 SHOP_USERNAME_ALREADY_OWNED. One the dictionary or the username rules refuse returns 409 USERNAME_BLOCKED.

A change racing the order can also return 409 USERNAME_RESERVED, USERNAME_RELEASE_EMBARGO or USERNAME_BLOCKED when the hold is taken. None of these refusals reaches Payriff.

When Payriff confirms the payment, the hold is fixed for 7 days before the order becomes paid. No other order can take the username in the 15 minutes after the order’s own 15, so a payment confirmed by then still finds the username held for it. One confirmed later finds it only while no other order has held the username in the meantime. A hold that still cannot be fixed a day after the order opened no longer waits: the order becomes paid without it, and delivery gives the username only if it is still free.

Delivery gives the account the username through the username ownership protocol. The purchase skips the 14-day wait between username changes and leaves the free change unused. The previous username is released as on any username change, and the account’s sessions receive User Update.

  • A delivery the username store cannot take now, because it is busy, rate limited or unavailable, is tried again later. A delivery still not made a day after the order opened, and an hour after its payment, fails the order with username_delivery_stalled, whatever kept it.
  • A retried delivery that finds the username already given by this order completes the order, even after the account changed to another username.
  • A delivery the username refuses fails the order with username_taken, username_not_allowed or username_already_owned, as Shop order failure reasons describes. A deleted account fails it with account_deleted.

A paid username card order that fails, including one with amount_mismatch, goes to the admin’s refund list. For amount_mismatch, the list shows the amount and currency Payriff reported. The admin refunds the payment in the Payriff panel and marks the order with Mark shop order refunded.

An admin can also refund a granted order. An account that still has the username from that order then loses it: it must pick a new username of 4 or more characters, and the username goes into an admin hold.

A premium package of 1, 3 or 12 months can also be bought as a Paddle subscription that renews every period until it is canceled. Only Paddle sells it, so only where Paddle sells to the request’s country. Cosmetic items, bundles and Visionary are never recurring.

  1. Get shop quote with billing_mode=recurring returns what every period charges, with interval_months and terms_version. The catalog carries the same offer in each package’s recurring_quote.
  2. Create shop order with billing_mode recurring and a recurring consent object opens the checkout. The server prices the offer again, and a consent whose terms_version, amount_minor, currency, interval_months or trial_days differs from that answers 409 SHOP_RECURRING_QUOTE_CHANGED before Paddle is asked. The client shows the new offer and asks for consent again. A recurring order for anything but premium:1, premium:3 or premium:12, or without a consent naming the same period as the SKU, fails validation with 400 INVALID_FORM_BODY. While it is pending, its payment_url fragment also carries &p=, every Plus period on sale to the order’s country as 1:9999,3:26999,12:94999,TRY (period:amount pairs, then the currency), for the payment page’s plan list only.
  3. The first payment binds the subscription to the account. Each renewal Paddle confirms moves paid_until to the end of the period it paid, and the account has premium until then.

When the offer carries trial_days, the checkout charges nothing and premium starts at once for that many days; the first charge comes when they end, and a cancel before then ends premium at the trial’s end. An account gets one trial, and none after an earlier subscription or trial, nor on a verified email address another account used a trial on; an account without a verified email gets none. The member discount on items and bundles starts with the first charge, and a refused first charge gives none. The shop tab may show a trial offer or the member discount up to five minutes late; the order reads both again.

An account takes one premium subscription at a time, and none on top of other premium time. While it cannot take one, the catalog’s viewer.recurring_eligibility says why, and a recurring order answers 409 SHOP_RECURRING_NOT_ELIGIBLE with that object as recurring_eligibility.

The routes below manage the account’s own subscription. None takes a subscription, customer or user ID from the client. They answer whatever the premium mode, the sales switches and the payment routing say, because a subscription Paddle still charges must always be cancelable.

FieldTypeDescription
acceptedbooleanAlways true
terms_versionstringThe terms_version of the quote the buyer accepted
amount_minorintegerWhat each period charges, in the minor unit of currency
currencystringUSD or TRY
interval_monthsintegerThe period: 1, 3 or 12 months
trial_days??integerThe offer’s trial_days; absent or null when it had none
FieldTypeDescription
billing_modestringAlways recurring
terms_versionstringThe version of the subscription terms
amount_minorintegerWhat each period charges, in the minor unit of currency, at the list price
currencystringUSD or TRY
interval_monthsintegerThe period: 1, 3 or 12 months
payable??payable amount objectThe same amount and currency
list_payable??payable amount objectThe same amount and currency
plus_payable??payable amount objectAlways null
trial_days??integerFree days before the first charge this account would get now; null for none
FieldTypeDescription
eligiblebooleanWhether the account can take a premium subscription now
reason?stringThe recurring eligibility reason, null when eligible
available_after?ISO8601 timestampprepaid_active and existing_subscription only: when premium time ends, null if it renews
ValueDescription
sales_closedPremium sales or new subscriptions are closed
region_unavailablePaddle does not sell to the request’s country
prepaid_activeThe account has premium time that has not ended
existing_subscriptionThe account has a live subscription, or a canceled one whose paid time has not ended
checkout_pendingAnother premium checkout of the account is open or being delivered
visionaryThe account has lifetime premium
purchase_disabledThe account has the purchase-disabled premium flag
ValueDescription
pendingThe first payment is not confirmed yet
activeThe subscription renews
past_dueA renewal payment failed, and Paddle retries it
pausedPaddle paused the subscription
canceledThe subscription ended, and nothing is charged again
needs_reviewSupport has to look at the subscription
FieldTypeDescription
statusstringThe Paddle subscription status
interval_monthsintegerThe period: 1, 3 or 12 months
amount_minorintegerWhat each period charges, in the minor unit of currency
currencystringUSD or TRY
paid_until?ISO8601 timestampThe end of the last paid period; premium lasts until then
next_billed_at?ISO8601 timestampWhen Paddle charges next, null once a cancel is scheduled or started by Micup, or the subscription ended
cancel_at?ISO8601 timestampThe cancel scheduled for the end of the paid period, null without one
can_cancelbooleanWhether Cancel premium subscription can schedule a cancel
can_reactivatebooleanWhether Keep premium subscription can take a scheduled cancel back
can_update_payment_methodbooleanWhether Change subscription payment method is open
GET/v1/shop/catalog

Returns the shop catalog object: the priced owned items, the premium items, the priced bundles, the premium packages, Visionary, the username cards, and the sales settings.

StatusBodyCondition
200shop catalog objectThe catalog was returned
403error responseFEATURE_NOT_AVAILABLE_SELF_HOSTED: the instance is not self-hosted in the mirror premium mode

30 requests per minute for each authenticated user, on the shop:catalog bucket.

GET/v1/shop/quote

Returns the shop quote object: what the account would pay for one product now. A quote reserves nothing and binds nothing. The order records the amount charged.

FieldTypeDescription
skustringThe product
billing_mode?stringone_time (default) or recurring, which prices a premium subscription
StatusBodyCondition
200shop quote objectThe quote was returned
400error responseThe SKU fails validation, returned as INVALID_FORM_BODY
403error responseSales are closed, or the instance is not self-hosted in the mirror premium mode
404error responseThe product cannot be bought now, returned as SHOP_ITEM_UNAVAILABLE
409error responseThe account already owns the item or an item of the bundle, returned as COSMETIC_ALREADY_OWNED
409error responseNo subscription is sold to the account, returned as SHOP_RECURRING_NOT_ELIGIBLE

A recurring quote is what every period of the subscription charges: the list price without a campaign, in the currency Paddle charges the account. It answers for premium:1, premium:3 and premium:12 only, and only where Paddle sells to the request’s country. Shop refusals lists the conditions behind each code.

60 requests per minute for each authenticated user, on the shop:quote bucket.

GET/v1/shop/username-cards/availability

Returns a username card availability object: whether the account could buy a username with a username card now. The answer never names the account that has the username, and it reserves nothing. Create shop order decides.

The server keeps the state of a checked username for up to 6 seconds, so the answer can be that old.

FieldTypeDescription
usernamestringThe username to check (at most 32 characters)
StatusBodyCondition
200username card availability objectThe username was checked
400error responseThe username is longer than 32 characters, returned as INVALID_FORM_BODY

30 requests per minute for each authenticated user, on the shop:username:availability bucket.

POST/v1/shop/orders

Opens a Payriff order for one product at the price the server computes now, and returns the shop order object with its payment page. The order is delivered once Payriff confirms the payment, as Payment and delivery describes.

An open order of the account for the same product at the same amount, for a bundle with the same items in the same order, and for a username card with the same username, is returned instead of a new one, so repeating the request opens no second payment.

  • The account must be claimed, and an unclaimed account returns 400 UNCLAIMED_ACCOUNT_CANNOT_MAKE_PURCHASES.
  • The account must have a verified email address, and an unverified address returns 403 PURCHASE_EMAIL_VERIFICATION_REQUIRED.
  • The account must not have the purchase-disabled premium flag, which returns 403 PREMIUM_PURCHASE_BLOCKED.
  • Every shop refusal applies, and a username card order also meets the limits in Username cards.
FieldTypeDescription
skustringThe product to buy
username?stringUsername card orders only: the username to buy, 1-3 lowercase letters or digits, as long as the card

A username card SKU without a username of its length, or a username with any other SKU, fails validation with 400 INVALID_FORM_BODY.

A Paddle order also takes a second field.

FieldTypeDescription
immediate_performance_consent?booleanIgnored; older clients still send it
billing_mode?stringone_time (default), or recurring to start a premium subscription
recurring_consent?recurring consent objectRecurring only, and required there: the recurring offer as the buyer accepted it
StatusBodyCondition
200shop order objectThe order was opened, or an open order for the same product and amount was returned
400error responseThe SKU or username fails validation, the account is unclaimed, or it holds its boost limit (MAX_PURCHASED_BOOST_SLOTS)
403error responseSales are closed, purchases are not allowed for the account, or the instance has no shop
404error responseThe product cannot be bought now, returned as SHOP_ITEM_UNAVAILABLE
409error responseItem already owned, three open orders, cannot subscribe now, recurring offer changed, or username card refused
429error responseA username card order limit applies, returned as RATE_LIMITED
503error responseThe server cannot open a Payriff order now, returned as SHOP_BUSY

The order is written as pending with the amounts of the one price computation, and its first scheduled check is set. A Visionary order holds a number, and a username card order its username, before Payriff is asked. A bundle order records the bundle’s items in item_ids, and a later change to the bundle, or its deletion, leaves them as they are. The catalog can be up to 60 seconds old, so a client compares item_ids with the items it showed before it opens the payment page. The Payriff order description names the order ID, the product and the US dollar price, so an admin can find the order with Get shop order in the Admin API. The description of a username card order never contains the username. When Payriff does not open the order, the order fails with provider_error, its Visionary number or username hold is freed, and the request returns 503 SHOP_BUSY.

3 requests per minute for each authenticated user, on the shop:order:create bucket. 20 requests per day for each authenticated user, on the shop:order:create:day bucket.

GET/v1/shop/orders/{order_id}

Returns one order of the account as the server last recorded it, as a shop order object. The route never asks Payriff, so it is the one to poll while a payment is open.

FieldTypeDescription
order_idsnowflakeThe ID of the order
StatusBodyCondition
200shop order objectThe order was returned
403error responseFEATURE_NOT_AVAILABLE_SELF_HOSTED: the instance is not self-hosted in the mirror premium mode
404error responseNOT_FOUND: no order has this ID, or the order belongs to another account

60 requests per minute for each authenticated user, on the shop:order:get bucket.

GET/v1/shop/codes/{code}Unauthenticated

Returns what a gift code gives, premium time or a cosmetic item, as a shop code object. It is the only route that shows an item code, because Get gift answers an item code with 404. Redeem gift redeems both kinds.

Every deployment serves the route. A self-hosted instance outside the mirror premium mode returns 403 FEATURE_NOT_AVAILABLE_SELF_HOSTED.

FieldTypeDescription
codestringThe code to look up (1-32 characters)
StatusBodyCondition
200shop code objectThe code was returned
403error responseFEATURE_NOT_AVAILABLE_SELF_HOSTED: the instance is self-hosted outside the mirror premium mode
404error responseUNKNOWN_GIFT_CODE: no code exists, the code was revoked, or its item no longer exists

The shop:gift:get bucket takes the limit of the gift:get bucket of Get gift: 60 requests per 10 seconds for each authenticated user or client IP address. The two buckets count separately, and a supplied credential only keys the bucket.

GET/v1/payriff/callback/{order_id}/{token}Unauthenticated

The browser lands here from the Payriff payment page. The route redirects it to the premium callback page of the web app with a status and no order detail. It never answers with an error.

FieldTypeDescription
order_idstringThe ID of the order
token1stringThe callback token of the order

1 32 random bytes in base64url, created with the order. It exists only in the callback address handed to Payriff. The server keeps its SHA-256 hash and compares in constant time, and the request and error logs write the path with the token masked

StatusBodyCondition
302emptyAlways, with Location set to /premium-callback?status= on the web app and one of the statuses below
  • success when the order is granted, or paid and waiting for delivery.
  • cancel when the order failed or was refunded.
  • pending in every other case: an unpaid or expired order, a token that does not match, an unknown or malformed order ID, or a server error.

A matching token queues a check of the order with Payriff, as Payment and delivery describes. Repeats within 30 seconds queue one check, and each order takes at most 10 callback checks an hour, so replaying a return link cannot spend the Payriff budget. The scheduled checks run whatever the callbacks do. Anything else queues nothing.

600 requests per minute for each client IP address, or for the authenticated user when a credential happens to be supplied, on the payriff:callback bucket.

POST/v1/payriff/callback/{order_id}/{token}Unauthenticated

Payriff calls this when a payment changes. Payriff signs nothing, so the body is never read. Only the order ID and the token in the path count, with the same path parameters and side effects as Return from Payriff. Returns a webhook received object.

StatusBodyCondition
200webhook received objectAlways, unless the caller accepts text/html
303emptyThe caller accepts text/html, and is sent where Return from Payriff would send it

600 requests per minute for each client IP address, or for the authenticated user when a credential happens to be supplied, on the payriff:callback bucket.

POST/v1/paddle/webhookUnauthenticated

Paddle is the second payment provider next to Payriff. Paddle posts a signed event here, and the route queues the ones the shop handles for the worker and returns a webhook received object. The Paddle-Signature header is the credential.

  • A deployment with no Paddle webhook secret configured returns 503. Paddle retries any non-2xx answer, so the event is delivered again once the secret is set.
  • A body over 1 MiB returns 413.
FieldTypeDescription
Paddle-Signature1stringts=<unix seconds>;h1=<hex>, the HMAC-SHA256 signature of the raw body

1 h1 is the HMAC-SHA256 of <ts>:<raw body> under the webhook secret, and a header with several h1 values is accepted when one matches. ts must be within 5 minutes of the server clock

StatusBodyCondition
200webhook received objectThe signature verified, including a redelivered event or an unhandled event type, which queue nothing
401error responseThe header is missing or malformed, the timestamp is stale, or the signature did not verify
413emptyThe body is over 1 MiB
503error responseNo Paddle webhook secret is configured

The response comes before processing. Only transaction.completed, adjustment.created and adjustment.updated events are queued, and any other verified event is acknowledged and dropped.

600 requests per minute for each client IP address, or for the authenticated user when a credential happens to be supplied, on the paddle:webhook bucket. The bucket is exempt from the global bucket.

POST/v1/paddle/subscription/cancel

Cancels the account’s own Paddle subscription and returns the Paddle subscription state object as Paddle answered. An active subscription is canceled at the end of its paid period: premium lasts until paid_until, and nothing is charged again. A past_due or paused one is canceled at once, as its paid time already ended, so Paddle stops retrying the failed renewal. A cancel already scheduled, or one Micup already owes Paddle, is returned unchanged, so repeating the request changes nothing. A subscription waiting on review, and a limited account, can cancel too. When the Paddle budget for subscription changes is spent, the cancel is kept as owed and sent by the next check, and the answer already shows it scheduled for paid_until. The route takes no body.

StatusBodyCondition
200Paddle subscription state objectThe cancel is scheduled, done or owed
400error responseNO_ACTIVE_SUBSCRIPTION: the account has no active, past_due or paused subscription
409error responseSHOP_SUBSCRIPTION_CHANGE_REFUSED: Paddle refuses the change now, as it does within 30 minutes of a renewal
503error responseSHOP_BUSY: Paddle did not answer

10 requests per minute for each authenticated user, on the paddle:subscription:manage bucket.

POST/v1/paddle/subscription/reactivate

Takes back a scheduled cancel of the account’s own Paddle subscription while its paid period lasts, so it renews again, and returns the Paddle subscription state object as Paddle answered. Without a scheduled cancel it changes nothing. The route takes no body.

StatusBodyCondition
200Paddle subscription state objectThe subscription renews again, or had no scheduled cancel
400error responseNO_ACTIVE_SUBSCRIPTION: the subscription has ended
403error responsePREMIUM_PURCHASE_BLOCKED: the account has the purchase-disabled premium flag
403error responseThe account is limited and the request returns ACCOUNT_LIMITED
409error responseSHOP_SUBSCRIPTION_CHANGE_REFUSED: the cancel was Micup’s own, the subscription waits on review, or Paddle refuses
503error responseSHOP_BUSY: Paddle did not answer, or the Paddle budget for subscription changes is spent

A cancel is Micup’s own when the server asked Paddle for it after a refund, a chargeback, a second subscription of the same account, a payment it did not accept, or the account’s deletion. Such a cancel is never taken back.

10 requests per minute for each authenticated user, on the paddle:subscription:manage bucket.

POST/v1/paddle/subscription/payment-method

Returns a redirect URL object whose url is a one-time link to the approved payment page. The server builds it from the transaction ID Paddle gives for the change, as ?_ptxn= and that ID (Paddle’s overlay, even while the deployment sets FLUXER_PAY_INLINE_LINK for orders), followed by the same #t= client-side token and &l= language a Paddle order’s payment_url carries when a token is configured. There Paddle’s checkout changes the card the account’s own subscription renews with, and while a renewal has failed it pays that renewal too. The response carries Cache-Control: no-store, and the server never stores the link. The route takes no body.

StatusBodyCondition
200redirect URL objectThe link was made
400error responseNO_ACTIVE_SUBSCRIPTION: the account has no live subscription
403error responsePREMIUM_PURCHASE_BLOCKED: the account has the purchase-disabled premium flag
403error responseThe account is limited and the request returns ACCOUNT_LIMITED
409error responseSHOP_SUBSCRIPTION_CHANGE_REFUSED: the subscription has a cancel of Micup’s own, waits on review, or Paddle refuses
503error responseSHOP_BUSY: Paddle did not answer, or the Paddle budget for subscription changes is spent

10 requests per minute for each authenticated user, on the paddle:subscription:manage bucket.