Music Video Marketplace · v1 · 2026-10-28

Partner payments guide

Take your customers' payments on your own checkout: the embed asks your page to take the payment, your server confirms the paid order, the video unlocks, and we bill you the wholesale price.

What changed in v1

First published version: the optional "partner takes the payment" model, covering setup, the postMessage contract, the signed server confirmation, prices, testing, billing and a go-live checklist.

Partner payments · v1
New

Sell our videos on your own checkout

Your customers pay you, on your site, your way. You tell us each order is paid, the video unlocks inside the embed, and we invoice you a fixed wholesale price per sale. You keep the difference.

This is optional and per partner. Partners who keep the default, where customers pay us inside the embed, are unaffected. Already live with us taking the payment? You can test first and move over in your own time (section 3). Signing your users in works exactly as in the embed guide, so read that first.

1
You charge

The customer pays you on your own checkout, at the price you choose.

2
You confirm

Your server sends us one signed call per paid order. The video unlocks straight away.

3
We bill you

Each live sale adds your wholesale price to a running total, charged to your saved card at the end of each billing period.

Keep one standard price across every video

We use a single standard price for every video in the catalogue, and we recommend you do the same: choose one price and use it for everything instead of pricing videos one by one. It is simpler for your customers, simpler for you, and it avoids mistakes. The amount is entirely up to you. See section 8 for how to set it.

Just keep it above your wholesale price (shown on your dashboard's Billing tab as Per purchase). We bill that amount on every live sale whatever your customer paid, so a sale priced below it loses you money. We do not block it, we only flag it in your ledger.

1How a sale works

  1. The customer clicks Buy in the embed. We reserve the sale (a purchase request, valid for 2 hours) and the embed shows "Complete your purchase in the checkout that just opened".
  2. The embed sends your page a MVM_PURCHASE_REQUEST message. Your page opens your checkout as an overlay or popup.
  3. The customer pays you, your way (card, PayPal, invoice, anything).
  4. Your server sends us a signed partner-confirm-purchase call for that order.
  5. We unlock the video. Your page then sends MVM_PURCHASE_COMPLETE to the embed, which shows the video as owned.
  6. We add the sale to your ledger at your wholesale price. A billing run charges your saved card at the end of each billing period.

Hard rule: never reload or navigate the page that holds the embed. The customer may have an unsaved edit inside it. Use an overlay or popup for your checkout.

2A worked example

Say your wholesale price is £99.00 per video, your billing period is 14 days, and you choose to sell each video for £149.00.

 
Day 2   Ana buys "Sunset Vibes"   pays you £149.00   you confirm  -> we add £99.00 to your running total
Day 5   Ben buys "Golden Dusk"    pays you £149.00   you confirm  -> running total £198.00
Day 9   Ana buys "Neon Rain"      pays you £149.00   you confirm  -> running total £297.00
Day 14  The period ends           we invoice £297.00 and charge your saved card
        Customers paid you £447.00, you owe us £297.00, and you keep £150.00 (£50.00 per sale).

If you set an early charge threshold of £250.00, we would not wait for day 14: the running total passes £250.00 on day 9, so we would invoice £297.00 then and start a new total after it.

If you had priced a video at £79.00 instead, you would still owe us the same £99.00 for it, and that sale would be flagged below_floor in your ledger.

If that card payment fails, your account shows payment failed and you can keep selling through the grace period. Pay the invoice or update your card from the Billing tab at any time; after the grace period, new live sales are refused until it is settled. Customers always keep what they bought.

Sales made with your test credentials are never added to the total. Only live sales are billed.

3Moving over from "MVM takes the payment"

Already live with customers paying us inside the embed? You do not have to change everything at once. We move you over in two waves, and the second one is yours to trigger.

  1. We clear you for it. We switch on partner payments for your account in our admin, and set your wholesale price. Nothing changes for your customers.Your live embed still has us taking the payment, exactly as before.
  2. You build and test, safely. Your test credentials now use the partner-payments flow, while your live credentials keep working the old way. You can run your own checkout end to end against the test embed (nothing is billed) as often as you like, without touching a single live customer. The dashboard's Billing tab shows a short readiness list: a test sale confirmed from your server, your websites listed, your wholesale price set, and a payment card on file.
  3. You switch when you are ready. The Billing tab has a button, Switch my live embed to my own checkout. It needs a card on file and a wholesale price; the rest of the list is advice. Pressing it moves your live embed to the flow in this guide, straight away, and we start billing your wholesale price on each live sale.
  4. You can switch back. The same tab has a button to put customers back on us taking the payment. Sales you have already confirmed stay on your bill; nothing new is added after you switch back.

Choosing your moment.

  • Switch at a quiet time, with your checkout live and your production credentials already in your server environment.
  • Customers who already have the embed open are asked to reload before their next purchase (they see "Purchases have moved to a new checkout. Please reload this page"). Anything they were in the middle of paying for through the old checkout can still finish.
  • Switching changes who takes new payments. It does not change what customers already own.
  • Your test credentials keep working the same way after you switch.

4Setup checklist

We set, in our admin: that partner payments are enabled for you, your wholesale price per video, billing currency, billing period, and optionally a default retail price and an early charge threshold (section 11).

You do, in your partner dashboard:

  1. Credentials: note your live and your test client ID and secret. Keep secrets on your server only.
  2. Credentials → Allowed websites: list every address that holds the embed (your live site and each place you test from). Use the address only, for example https://www.yoursite.com, with no page path. A page that is not listed never receives the Buy message.
  3. Billing: add a payment card (a Stripe-hosted page; we never see the number). Live sales are refused until a card is on file. Test sales do not need one.
  4. Build the page code (section 6) and the server confirmation (section 7), and try it with your test credentials (section 10).

5Messages between the embed and your page

Messages use window.postMessage. Only accept events whose origin is exactly https://musicvideomarketplace.com and whose source is your iframe's contentWindow, and always send replies with the target origin https://musicvideomarketplace.com.

From the embed, when the customer clicks Buy:

 
{ "type": "MVM_PURCHASE_REQUEST",
  "requestId": "2f0c7c1e-9a2b-4c3d-8e4f-5a6b7c8d9e0f",
  "videoId": "6a1f1b2e-0c2d-4e5f-8a9b-0c1d2e3f4a5b",
  "title": "Sunset Vibes",
  "price": 129.00,            // your display price, or null: show your own
  "currency": "GBP",
  "user": { "externalUserId": "user_8841", "email": "customer@example.co.uk" },
  "isTest": false }

From your page, back to the embed (same requestId):

 
{ "type": "MVM_PURCHASE_COMPLETE",  "requestId": "…" }   // only after your server's confirmation returned success
{ "type": "MVM_PURCHASE_CANCELLED", "requestId": "…" }   // the customer closed your checkout

MVM_PURCHASE_COMPLETE only makes the embed check straight away: the video is unlocked by your server's confirmation, never by a browser message.

6The code on your page

 
window.addEventListener('message', async (event) => {
  const iframe = document.getElementById('mvm-embed');
  if (event.origin !== 'https://musicvideomarketplace.com' || event.source !== iframe.contentWindow) return;
  const msg = event.data;
  if (!msg || msg.type !== 'MVM_PURCHASE_REQUEST') return;

  const paid = await openYourCheckout({ title: msg.title, price: msg.price, currency: msg.currency });
  if (!paid) {
    iframe.contentWindow.postMessage({ type: 'MVM_PURCHASE_CANCELLED', requestId: msg.requestId }, 'https://musicvideomarketplace.com');
    return;
  }
  const res = await fetch('/api/mvm-confirm-purchase', {       // your own server endpoint (section 7)
    method: 'POST', headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ requestId: msg.requestId, videoId: msg.videoId, amount: paid.amount, currency: paid.currency }),
  });
  if (res.ok) iframe.contentWindow.postMessage({ type: 'MVM_PURCHASE_COMPLETE', requestId: msg.requestId }, 'https://musicvideomarketplace.com');
});

Your server endpoint must take the customer's id and email from your own signed-in session, not from the browser's request body, and use the same partner_external_user_id you signed into the embed link.

7Confirming a paid order (server to server)

POST https://musicvideomarketplace.com/api/public/partner-fn/partner-confirm-purchase

HeaderValue
Content-Typeapplication/json
x-mvm-client-idYour client ID (your test client ID in test mode)
x-mvm-timestampISO-8601 time, within ±5 minutes of ours
x-mvm-signatureLowercase hex HMAC-SHA256 of `${timestamp}.${rawBody}` with your client secret

Sign the body exactly as you send it: build the JSON string once and send that same string.

Body fieldRequiredMeaning
partner_order_refYesYour order number. Unique per partner. A repeat returns the same sale, so retrying is always safe
video_idYesThe video from the purchase request
partner_external_user_idYesThe customer id you signed into the embed link
emailNoCustomer's email (contact detail only)
amount, currencyNoWhat the customer paid you, e.g. 129.00 and GBP
paid_atNoISO-8601 payment time
request_idRecommendedThe requestId from the Buy message: lets the embed resume after a reload
import crypto from 'node:crypto';

export async function confirmPurchase(order) {
  const body = JSON.stringify({
    partner_order_ref: order.id, video_id: order.videoId, partner_external_user_id: order.userId,
    email: order.email, amount: order.amountPaid, currency: order.currency,
    paid_at: new Date().toISOString(), request_id: order.requestId,
  });
  const timestamp = new Date().toISOString();
  const signature = crypto.createHmac('sha256', process.env.MVM_CLIENT_SECRET)
    .update(timestamp + '.' + body).digest('hex');
  const res = await fetch('https://musicvideomarketplace.com/api/public/partner-fn/partner-confirm-purchase', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json', 'x-mvm-client-id': process.env.MVM_CLIENT_ID,
               'x-mvm-timestamp': timestamp, 'x-mvm-signature': signature },
    body,
  });
  const data = await res.json();
  if (res.ok || (res.status === 409 && data.error === 'already_owned')) return data;
  throw new Error(res.status + ' ' + data.error + ': ' + data.message);
}

A successful answer looks like this (abridged):

 
{ "success": true, "transaction_id": "7c9e6679-…", "status": "pending",
  "partner_order_ref": "ORD-2026-000123", "video_id": "6a1f1b2e-…",
  "wholesale_amount": 10.00, "currency": "GBP", "customer_amount": 25.00,
  "is_test": false, "idempotent": false, "flag": null,
  "message": "Purchase confirmed." }

A 200 answer includes wholesale_amount (what we will bill you for this sale) and is_test. Errors come as { "error": "<code>", "message": "…" }:

StatuserrorWhat to do
401missing_signature, stale_timestamp, invalid_client_id, invalid_signatureCheck the secret, that you signed the exact body, and your server clock
402billing_requiredAdd a card on the dashboard's Billing tab. Test calls are not affected
402billing_suspendedA charge failed and the grace period passed. Pay the open invoice or update the card
403partner_inactive, wrong_payment_modelYour account is not active, or is not set to "The partner takes the payment". Contact us
404request_not_foundThe request_id is not yours
409already_ownedThe customer already owns it. Not billed. Treat as success, and refund them if you charged them again
409video_unavailableSold out, hidden or not yet released. Not billed. Refund your customer
409request_mismatchThe request belongs to a different video or customer, or you mixed test and live credentials
5xxinternal_errorRetry with the same partner_order_ref: you can never create two billable sales for one order

8Setting your prices

Two prices matter, and it helps to keep them apart. Your wholesale price is what we bill you per sale (after any discount we have agreed). Your selling price is what your customer pays you. We take no percentage of it and never see your checkout.

The embed only displays a price. Your checkout is what charges it.

Whatever you set below is shown on the Buy button inside the embed and sent to your page as price. It never charges anyone. The amount your customer is actually charged is whatever your own checkout asks for, so make sure the two match.

Choose how the embed shows your price

OptionHowBest for
One standard price for every video (recommended)On your dashboard, open Billing then Your prices, enter your standard price and save. It applies to every video, with nothing to send from your server.Almost everyone. One number, changed in one place.
Exceptions for particular videosAdd prices to the signed embed payload (below). An exception wins over your standard price for that video.A launch offer or a bundle on a few videos.
No price in the embedLeave Your prices empty and send no prices. The message's price is null and your page shows its own price.When your checkout already shows the price clearly.

Per-video exceptions

Add a prices object to the payload you sign for the embed link. Keys are our video ids, values are amounts in your billing currency:

Signed embed payload
{ "partner_external_user_id": "user_8841", "email": "customer@example.co.uk", "timestamp": "2026-10-28T10:00:00.000Z",
  "prices": { "6a1f1b2e-0c2d-4e5f-8a9b-0c1d2e3f4a5b": 149.00 } }
  • Up to 200 videos per link. Invalid entries are ignored. For the whole catalogue use your standard price instead.
  • A price you change on your dashboard applies to embeds opened after you save. A customer who already has the embed open sees the old price until they reload.
  • Reel and Behind-the-Scenes are not sold separately: every deliverable is included in the one price.

The wholesale floor

  • Your floor is your wholesale price. Your dashboard will not let you save a standard price below it.
  • Nothing stops a per-video price or your own checkout from going below it. Such a sale is still confirmed and still billed at the wholesale price, and it is marked "flag": "below_floor" in your ledger. You simply make a loss on it.
  • Sales are always billed in your billing currency. If a customer pays you in another currency, report it in amount and currency for your own records.

9Refunds and mistakes

There are no refunds or voids on our side: a confirmed live sale is always billable. Decide before you confirm.

  • Confirm only after the customer has actually paid you. A failed or abandoned payment must not call us. Send MVM_PURCHASE_CANCELLED instead.
  • If you refund a customer after confirming, the video is already unlocked and the sale is still on your bill. Treat refunds as your own cost. If an order was confirmed by mistake, contact us before the period ends.
  • If our answer is video_unavailable or already_owned, nothing is billed. Refund the customer if you charged them.
  • If the confirmation fails after the customer paid (network error, 5xx), retry the same call with the same partner_order_ref. It is safe: you can never create two billable sales for one order. Keep retrying from a queue, not only from the customer's browser.
  • If the customer's page reloads during the payment, the embed reopens its waiting panel and re-asks your page to open the same checkout. Your page should ignore a repeated requestId it has already completed.
  • A video can sell out while a customer is paying you. We check availability when you confirm, which is why video_unavailable exists.

10Testing before you go live

  • Use your test client ID and secret (dashboard → Credentials) to sign the embed link and to confirm purchases. Those sales are marked as test, belong to test customers, and are never billed. The card requirement does not apply.
  • The embed shows a yellow TEST MODE bar. Test customers may use any address, including example.com.
  • You do not need a payment provider to test. Build a fake checkout: a "Pay (test)" button that always succeeds, followed by the same signed server call a real checkout would make. Only the card is pretend.
  • Test sales cannot unlock a live customer's request, and live credentials cannot confirm a test one (request_mismatch), so keep the embed link and the confirmation on the same mode.
  • To prove billing end to end, ask us for a small wholesale price (for example 1.00), put a card on file, make one live sale, and we will run the billing for you. The invoice can be voided afterwards.

The How to embed tab ticks off each step of your test run as we see it.

11What we bill

Setting (set by us)What it means
Wholesale price per videoWhat we charge you for each confirmed live sale, less any partner discount. Also your price floor
Billing currencyThe currency of the wholesale price and your invoices
Default retail priceOptional price shown to your customers in the embed unless your page sends its own
Billing period (days)How often you are billed. Sales in a period are added up and charged in one invoice when it ends
Grace days after a failed chargeHow long you may keep selling after a failed charge before live sales are refused. Paying or updating your card lifts it. Customers keep what they bought
Early charge thresholdOptional safety limit: if the amount owed reaches this before the period ends, we bill straight away
  • Add and update your card on the dashboard's Billing tab. It also lists every sale and invoice, with a link to view or pay each invoice, and an export to CSV.
  • A failed charge marks the account payment failed; sales continue during the grace period, then live sales return 402 billing_suspended until the invoice is settled.
  • Invoices are issued without tax unless we have switched tax on for partner invoices.

12Go-live checklist

  • Every address holding the embed is under Allowed websites.
  • A card is on file and the Billing tab shows Active.
  • Your production client ID and secret are in your server environment only.
  • Your checkout opens as an overlay or popup and never reloads or navigates the page.
  • Every paid order calls partner-confirm-purchase from your server with a stable partner_order_ref.
  • Your page sends MVM_PURCHASE_COMPLETE only after that call succeeded, and MVM_PURCHASE_CANCELLED if the customer backs out.
  • You refund customers when we answer video_unavailable.