1.Checklist
- Connect your Dodo business with an API key, and select the products you want to make offers for on the Products page.
- Turn on Card-optional at $0 price for every subscription product. Plucker can do this for you from Products.
- Give each product’s Dodo brand a public HTTPS website, or set an after-payment URL on the product in Plucker.
- Bill subscriptions once a month or once a year.
- Make your app recognise purchases made with a Plucker coupon. Give your developer, or your coding agent, the brief below.
2.How a coupon reaches your buyer
When you issue a coupon, Plucker creates a Dodo discount code limited to that product. When your buyer opens the link, Plucker opens a Dodo checkout with the code applied and the buyer’s email filled in. Dodo takes the payment, or starts the subscription for free, and sends the buyer back to your website.
Your app never sees that checkout. It learns about the purchase the way it learns about any Dodo purchase: from Dodo’s webhook, or from reading Dodo. That purchase carries Plucker’s coupon reference instead of your app’s own details, which is why your app needs the small change described below.
3.Connecting your Dodo business
Create an API key in your Dodo dashboard under Developer settings, in live or test mode, and paste it into Plucker. Dodo keys give full access to the business, which Plucker needs: it reads your products and brands, creates and removes discount codes, opens checkouts, and registers its own webhook so it can confirm purchases.
Plucker lists every product Dodo reports as available. Select the ones you want to make offers for; the create-coupon page only offers apps with at least one selected product. Plucker groups products into apps by the first word of their name, so a name like “Acme Pro (monthly)” works best, and two apps should not start with the same word.
Plucker understands prices billed once a month, once a year, or once only. A plan billed, say, every three months still takes coupons, but is labelled “other”.
4.Free coupons need no card
A 100%-off coupon on a subscription is the most common gift. Unless the product says otherwise, Dodo still makes the buyer save a card for the renewal, and verifying a card for $0 often fails at the bank’s security step. Some payment methods, such as Pix, cannot pay for a subscription at all.
Turning on Card-optional at $0 price in the product’s pricing fixes this. When nothing is due today, the subscription starts without a card, and Dodo emails the customer to add one before the first real charge. Nothing changes for paying customers or existing subscribers.
Plucker checks this for every subscription product and offers a one-click fix on the Products page. It will not issue a 100%-off coupon for a subscription that still needs a card. One-time products have no such setting and need none: a free one-time checkout does not ask for a card.
5.The page buyers return to
After checkout, Dodo sends the buyer to your product’s after-payment URL, or to the website on its Dodo brand if none is set. It must be a public HTTPS page. The buyer arrives with only Dodo’s payment or subscription reference, not with anything your app created, so the page should confirm the purchase from that reference or simply thank them. Plucker cannot issue a coupon for a product with no such page.
6.Confirmed redemptions
Plucker registers its own signed webhook in your Dodo business and marks a coupon as redeemed when Dodo reports a payment or a new subscription with its code. Your own webhooks stay as they are. If Plucker could not register its webhook, the product shows a warning; syncing the connection retries it.
7.What your app must do
Because Plucker opens the checkout, the purchase your app receives has no link to an account in your app. Your billing code has to adopt it:
- Check the product is one of yours first. Every app in a Dodo business receives every app’s purchases.
- Match the buyer by the email on the Dodo customer. Ignore the Plucker workspace reference: it identifies whoever issued the coupon, not the buyer.
- Never let a coupon purchase replace a subscription the account already pays for.
- If no account can take it yet, because the buyer pays first and signs up later, keep the purchase and grant it when that account appears. Never discard it.
8.For your coding agent
Paste this into your coding agent, or hand it to your developer. It describes the whole change without assuming anything about your stack.
# Make this app compatible with Plucker coupons (Dodo Payments)
Plucker issues Dodo discount codes for this app's products and opens the Dodo checkout itself. This app never sees that checkout. A purchase made with a Plucker coupon reaches this app only through Dodo: as a subscription (recurring products) or a payment (one-time products) whose metadata carries `plucker_coupon_id` and `plucker_workspace_id`, none of this app's own metadata, no checkout record of this app, and usually a Dodo customer this app has never seen.
## Adopt the purchase
Do this in the code that turns a Dodo subscription or payment into access. The webhook handler and any reconcile or sync job should share that code.
1. Product first. Before any owner lookup, check that the product belongs to THIS app. One Dodo business often sells several apps, and every app's webhook receives every app's purchases. A product that is not ours is ignored and nothing is recorded.
2. Recognise the outside sale by `plucker_coupon_id` in the metadata. Never use `plucker_workspace_id` for ownership: it identifies the Plucker workspace that issued the coupon, not the buyer.
3. Find the account by the Dodo customer's email, normalised the way this app stores emails (for example trimmed and lowercased).
4. Never take over an account that already pays for a different live subscription. Do not replace or cancel the subscription it pays for; leave the coupon purchase unclaimed instead (step 5).
5. Keep what no account can take yet. Usually the buyer paid first and signs up later. Store the subscription or payment id, the Dodo customer id, the lowercased email, the reason, first and last seen, last attempt, and claimed at / claimed by. Write it so it survives the failed apply (after the transaction's rollback).
6. Claim it later: from the periodic sync or cron (join unclaimed records to accounts by email first, so a run with nothing waiting makes no API call, and retry each record at most about hourly), and when the account signs up or opens its billing page. Stamp the claim in the same transaction that grants access.
7. Never drop it. A handler that logs "no user" and acknowledges the event loses the purchase for good.
8. One-time products follow the same rules for the payment, keyed on the payment id.
## Dodo product settings
- Every recurring price sets `zero_amount_payment_method_optional: true` ("Card-optional at $0 price"). Without it, a 100%-off coupon makes the buyer save and 3-D Secure-verify a card for $0, which often fails, and Pix cannot pay for a subscription at all. Leave `trial_payment_method_optional` unchanged unless you sell a card-optional free trial. If a script manages the catalog, have it write this field and treat a product without it as drifted.
- Bill every recurring price once per month or once per year (`payment_frequency_count: 1`).
- Give the product's brand a public HTTPS website, or set an after-payment URL for the product in Plucker. Buyers return there after checkout with only Dodo's query parameters, so that page must work for someone with no checkout record of this app (confirm by `payment_id` or `subscription_id`, or show a general success message).
## Optional: your own checkout page
If the product has an "app checkout URL" in Plucker, Plucker sends the buyer there with `discount_code` and `plucker_coupon_id` query parameters instead of opening a Dodo checkout. That route must:
- require the buyer to sign in;
- use `discount_code` only by passing it into its own checkout session's `discount_codes`;
- copy `plucker_coupon_id` into the session metadata next to the app's own keys;
- otherwise run the app's normal checkout.
## Verify without real money
- Recurring products: `checkoutSessions.preview` with a 100% discount code must return `payment_method_required: false` and a total of 0.
- One-time products: the preview reports a card as required, but a real $0 one-time checkout does not ask for one. Run a real test-mode checkout with a 100% code instead: it should reach your success page without a card, and access should be granted.
- Run the adoption code against a disposable database, or inside a transaction you roll back, with fake subscriptions tagged `plucker_coupon_id`. Cover: an existing account matched by email in a different letter case; no account yet, so the purchase is kept and then claimed after sign-up; an account paying for another live subscription, which is not taken over; another app's product, which is ignored; and a purchase without Plucker metadata, whose behaviour is unchanged.
9.Optional: your own checkout page
If purchases must be tied to a signed-in account, set an app checkout URL on the product in Plucker. Plucker then sends buyers to that page with discount_code and plucker_coupon_id in the address, and your page opens the Dodo checkout itself with the code applied. The brief above explains what that page must do.
10.When a purchase is not confirmed
The Coupons list marks a coupon “Not confirmed” when its checkout was opened more than an hour ago and Dodo has not reported a purchase with its code. Often the buyer simply did not finish. If they say they paid:
- Find the payment in your Dodo dashboard and check its status.
- If it is still waiting for a card on a subscription, turn on card-optional at $0 for the product and send the buyer the link again; an unused code can be used once more.
- If it succeeded but the buyer has no access, your app did not adopt the purchase. See what your app must do.
Still stuck? Write to support@mattmade.ai. For App Store subscriptions, see the Apple setup guide.