• Categories
  • Recent
  • Tags
  • Popular
  • Solved
  • Unsolved
  • Users
Skins
  • Light
  • Cerulean
  • Cosmo
  • Flatly
  • Journal
  • Litera
  • Lumen
  • Lux
  • Materia
  • Minty
  • Morph
  • Pulse
  • Sandstone
  • Simplex
  • Sketchy
  • Spacelab
  • United
  • Yeti
  • Zephyr
  • Dark
  • Cyborg
  • Darkly
  • Quartz
  • Slate
  • Solar
  • Superhero
  • Vapor

  • Default (Darkly)
  • No Skin
Collapse
brainCloud Forums

Automated Promotions with no discount — "Not for Sale" workaround and purchase count tracking

Scheduled Pinned Locked Moved Unsolved General
8 Posts 3 Posters 75 Views
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • G Online
    G Online
    gyutaelee
    wrote last edited by
    #1

    Hi BrainCloud team,

    We're implementing Automated Promotions to gate specific products behind user segments and time windows — but without any price discount. The goal is simply to make a product available (at its regular App Store price) for a limited time to eligible users, not to offer it at a reduced price.

    The console constraint we ran into:

    When setting up a Cash Product in an Automated Promotion, the dashboard enforces that the promotional price must differ from the default price. Since we don't intend to offer a discount, we can't configure a valid second price tier.

    Our current workaround:

    We set the promotional price to "Not for Sale" (priceId: -2). This lets us pass validation and receive the itemId in the GetEligiblePromotions response, which we use to look up the product. The actual purchase goes through the App Store at the product's standard list price — our client ignores the priceId and referencePrice entirely.

    Our question:

    With this "Not for Sale" workaround, does the Promotion's purchased counter increment correctly when a successful IAP purchase is verified via verifyPurchase?

    We're relying on purchased >= maxPurchases to prevent the promotion from re-appearing after a user has already bought the product. If the counter doesn't increment in this setup, we need to handle purchase tracking ourselves.

    Feature request:

    Is there a plan to support promotions that sell products at their default/list price without requiring a discounted price tier? For use cases like timed exclusives or segment-gated access (rather than sales), this would remove the need for workarounds.

    Thanks in advance!

    1 Reply Last reply
    0
  • J Offline
    J Offline
    JasonL bitHeads
    wrote last edited by
    #2

    If your promotion is for a cash product and the promotional product is set to Not for Sale, you need to complete the flow by calling verifyPurchase in brainCloud with the purchasePayload from the store.

    As long as that payload contains the promotionId you received from GetEligiblePromotions, the promotion will be correctly attributed and the server-side flow will reach the counter increment logic.

    Also make sure you set maxPurchases on the promotion configuration page so the system knows when to stop returning that promotion for the user.

    As for your feature request to allow promotions without requiring a discount, I will forward this to our team for review.

    1 Reply Last reply
    0
  • Paul WinterhalderP Offline
    Paul WinterhalderP Offline
    Paul Winterhalder brainCloudAdmin
    wrote last edited by Paul Winterhalder
    #3

    Hi - I'm probably missing the subtleties of your use case.

    But in your scenario, what folks would normally do is:

    • Set the product as NOT FOR SALE by default.
    • Then have Promotion set it as AVAILABLE at the STANDARD price point.

    Is there a reason that you don't set the product as NOT FOR SALE initially?

    1 Reply Last reply
    0
  • G Online
    G Online
    gyutaelee
    wrote last edited by gyutaelee
    #4

    Thank you for the responses. I'd like to clarify my situation and ask more specific follow-up questions.

    My current setup:

    • Product default price: Standard price (normally available for purchase)
    • Promotion price override: Not for Sale (priceId: -2)
      • Reason: The console requires the promotion price to differ from the default price, so this is used as a workaround to pass validation
    • Actual purchase price: Provided directly by Apple App Store / Google Play
      In other words, I'm using promotions not to change the price, but to control access for specific user segments and track purchase counts.

    Question 1: Is using "Not for Sale" as a promotion price override a supported pattern?

    With the setup described above — where the promotion price override is set to Not for Sale (priceId: -2) — does BrainCloud's purchase counter and maxPurchases limit work correctly? Is this a supported use case?

    Question 2: How exactly should promotionId be passed during purchase?

    JasonL mentioned:

    "As long as that payload contains the promotionId you received from GetEligiblePromotions, the promotion will be correctly attributed and the server-side flow will reach the counter increment logic."

    However, in a standard IAP flow:

    1. Call GetEligiblePromotions → receive promotionId
    2. User completes payment on Apple App Store / Google Play → store returns a receipt (binary/base64 data generated by Apple/Google)
    3. Call AppStoreVerifyPurchase (or equivalent) with that receipt
      The receipt from step 2 is generated by Apple/Google — I cannot inject custom fields like promotionId into it.

    So my question is: in which parameter and in what format should promotionId be passed in the verifyPurchase call? A concrete example of the API call with promotionId correctly included would be extremely helpful.

    Summary:

    • Question
    1. Is setting the promotion price override to Not for Sale (priceId: -2) a supported pattern, and will the purchase counter work correctly?
    2. In the verifyPurchase API call, which parameter carries the promotionId, and in what format?
    1 Reply Last reply
    0
  • G Online
    G Online
    gyutaelee
    wrote last edited by
    #5

    Following up with an additional question related to this thread:

    We're testing the promotion and purchase flow in the iOS Sandbox environment, and we're not seeing the Purchase Count increment after a successful sandbox purchase — even when calling verifyPurchase with the promotionId included.

    Is purchase count tracking supported in the iOS Sandbox environment, or is it only tracked for real (production) purchases?

    If sandbox purchases are not counted, is there a recommended way to test the maxPurchases limit before going to production?

    Any guidance would be appreciated. Thank you!

    1 Reply Last reply
    0
  • J Offline
    J Offline
    JasonL bitHeads
    wrote last edited by
    #6

    Did you call CachePurchasePayloadContext() with the promotionId before calling verifyPurchase() ? That’s the piece that passes the promotion ID through and should trigger an increase in the maxPurchases counter.

    1 Reply Last reply
    0
  • Paul WinterhalderP Offline
    Paul WinterhalderP Offline
    Paul Winterhalder brainCloudAdmin
    wrote last edited by
    #7

    Question 1

    Let me clarify how this is normally handled.

    The client app gets a list of the available items for purchase by calling GetSalesInventory(). That call take into account who the user is, what segments they are in, etc. to determine which Cash Products to return in that call.

    So if you want to use segments to determine who can purchase an item, you would normally:

    • Set the item as "Not for Sale" as it's default price
    • Then in the segmented Promotions, you set the sale (in this case "Regular") price.

    In that way - people normally cannot buy the product - but it the user is in the segment covered by the promotion - it WILL appear at what you call the "regular price" for that user when they call GetSalesInventory().

    Do you get how this does what you are asking for?

    Question 2 - How is promotionId to be passed.

    The promotion is automatically taken into account when calling GetSalesInventory(). The trick is to still know a promotion is involved when we are processing the purchase receipt. That is accomplished by caching the new "payload" field.

    So the client should:

    • Call GetSalesInventory() to determine what products to show to the user
    • Bring up their store UI so the user can view things. Probably involves calling the platform's store APIs to get localized prices of everything.
    • When the user selects an item to purchase, the client app world:
      • Call AppStore.CachePurchasePayloadContext() with the "payload" field that was associated with the item the user has chosen.
      • then call the platform's appstore api to initiate the purchase.

    Then when brainCloud is processing the purchase it will retrieve the cached "payload" information - which includes information about the promotionId that was involved (if there was one).

    So note - in this flow you don't really NEED to call GetEligiblePromotions() - though that can be handy if you need to use that to show some banners in your store or something.


    Regarding counting of sandbox purchases - I don't think we include those in the stats. We record the purchase so that you can verify that it is happening end-to-end, but we now keep those totals out of the stats.

    I hope that helps!

    1 Reply Last reply
    0
  • G Online
    G Online
    gyutaelee
    wrote last edited by gyutaelee
    #8

    Thank you for the responses. Based on the discussion, we've implemented the following:

    • Product default price: standard App Store price
    • Promotion price override: "Not for Sale" (priceId: -2) — workaround to pass console validation requiring a price difference
    • Purchase verification: server-to-server (S2S) VERIFY_PURCHASE, no explicit promotionId in the request
    • Promotion gating: via GetEligiblePromotions() on the client

    We're observing that the purchased counter increments correctly after purchase, and the promotion does not reappear after app restart.

    We believe the reason automatic attribution works without calling CachePurchasePayloadContext() is that we set Apple's appAccountToken (a UUID embedded in the StoreKit 2 signed transaction) to the brainCloud profileId before initiating the native IAP purchase. When brainCloud receives the JWS receipt during S2S VERIFY_PURCHASE, it can extract the appAccountToken, identify the user, look up their active promotions, and increment the counter automatically.

    Two quick questions:

    1. Is this understanding correct — does brainCloud use the appAccountToken in the StoreKit 2 JWS to perform automatic promotion attribution, making CachePurchasePayloadContext() unnecessary in this flow?
    2. When we extend this to Android (Google Play), there is no equivalent of appAccountToken in the receipt. What is the recommended approach to ensure promotion attribution works correctly in that case — should we call CachePurchasePayloadContext() before the purchase, or is there another mechanism?
    1 Reply Last reply
    0

  • Login

  • Login or register to search.
  • First post
    Last post
0
  • Categories
  • Recent
  • Tags
  • Popular
  • Solved
  • Unsolved
  • Users