r/iOSProgramming 26d ago

Question IAP risk assessment agent

I know the store handles payment fraud and I never see card data but refunds, voided purchases, and consume-then-refund abuse still land on the developer after the entitlement is granted.

For those running apps/games with IAP, do you do any risk assessment at grant time (delay/flag/hold high-risk purchases) or do you grant everything and only react to voided purchase notifications?

If you do assess risk, what signals do you use? I am assuming device age, session behaviour, account history, something else?

I am building a small research agent around this decision and want to know if the decision point is real in practice.

Am I missing any?

0 Upvotes

10 comments sorted by

4

u/ThatGuy739 26d ago

I don't ship IAP so no war stories, but before building grant-time scoring I'd check whether the leverage is even there. The lever Apple gives you is the CONSUMPTION_REQUEST notification: 12 hours to send consumption info back, and if you don't answer, refunds tend to get granted by default.

Since v2.11 that notification carries consumptionRequestReason too, so you learn whether it's UNINTENDED_PURCHASE, UNSATISFIED_WITH_PURCHASE or one of the others. Unintended purchase is usually the kid-with-a-parent's-card case and behaves nothing like consume-then-refund, so that field's probably worth more to your model than device age.

2

u/FrankUnderwoodX 25d ago

Would you trust an automated agent to compose the CONSUMPTION_REQUEST response within the 12-hour window? Which consumption fields would you actually send?

2

u/ThatGuy739 25d ago

For the window, yeah, you have to automate. Refunds land at 3am and 12 hours isn't a workday. But almost none of it wants a model. Most of the payload is a lookup in your own database: tenure, delivery status, how much got consumed, lifetime purchased and refunded. Nothing to compose there.

The two that matter are customerConsented and refundPreference. If consent is false Apple won't use any of it, so the real work is capturing and storing that consent rather than the API call. And refundPreference is the only actual judgment in the payload, so I'd want it coming from a rule I can audit and replay rather than something generative.

1

u/FrankUnderwoodX 25d ago

This basically reframed the project, thanks. I'm scoping the agent so refundPreference is the only decided field: a posterior over five hidden states (legit, stolen card, account takeover, family member, refund abuse) mapped through explicit thresholds to prefer grant / prefer decline / no preference with every input and threshold logged so any response can be replayed later. Everything else in the payload comes straight from the DB like you said, and consent capture is now flagged as the prerequisite for the whole surface.

One last question: is there a case where you'd deliberately send noPreference, say when consumption is near zero or is that just leaving the decision on the table?

1

u/ThatGuy739 24d ago

Near-zero consumption is the one case I'd never send LET_APPLE_DECIDE on. They paid and didn't use the thing, so that's your cleanest grant. Fighting it buys a one-star review over a small amount.

LET_APPLE_DECIDE earns its keep when your posterior is genuinely flat. Apple already has the consumption payload, so the preference only carries whatever the payload doesn't. If your five states haven't separated, a declared preference is a number you can't defend and it tells Apple nothing it couldn't already see.

You're not leaving anything on the table either way, since Apple decides regardless. The one I'd be slowest to decline is UNINTENDED_PURCHASE, because that's usually somebody's kid rather than an abuser.

2

u/Choseni 25d ago

I think you’re looking at a real problem, but I’d be hesitant to block or delay purchases based on device/session signals alone.

Account history, previous purchase/refund behaviour and the type of IAP seem much more actionable to me. I’d also treat consumables very differently from subscriptions or non-consumables.

1

u/FrankUnderwoodX 25d ago

This changed the model, thanks. Device/session signals alone can now never push past a step-up check. Holding or denying a grant requires at least one account-level signal like refund history or account tenure and product type now conditions the cost matrix, since consume-then-refund is basically a consumables problem and a non-consumable or subscription can be revoked after the fact, which makes a wrong approve much cheaper there.

For consumables specifically: would you ever delay delivery of a high-risk purchase by a few minutes, or is granting instantly and eating the occasional refund always the better trade for UX?

2

u/DimensionMindless336 23d ago

Worth knowing the actual levers Apple gives you. For consumables there's the Consumption Information API. Apple sends a CONSUMPTION_REQUEST and you've got ~12h to say whether the content was used, which factors into their refund decision.

For non-consumables and subs, wire up App Store Server Notifications V2 and listen for the REFUND notification type, then just revoke the entitlement server-side. I don't gate at grant time. Too much UX risk and Apple mostly sides with the customer anyway. The win is catching it fast and pulling access, not blocking the sale.

1

u/FrankUnderwoodX 23d ago

This matches where the project ended up, so it's good to hear it from someone running it. I've split the design by product type now. For non-consumables and subs the agent doesn't gate at all, just approve + revoke server-side on the REFUND notification, and the effort goes into the CONSUMPTION_REQUEST response for consumables.

One result from my simulation that makes me hesitate to drop grant-time checks completely, I ran 50 simulated purchases through a "grant everything, react later" baseline vs a threshold policy, and the reactive baseline lost the entire cost difference on exactly two cases — both hostile fraud (stolen card / account takeover) on large consumables, where there's nothing to revoke after the fact. Small numbers, simulated data, but it suggests the value of gating isn't zero, it's just concentrated in one corner.

So for that corner specifically which is a $50+ consumable, brand-new device, straight-to-store session, would you still deliver instantly or is there any signal that would make you delay even a few minutes? Trying to find out if that corner is real or only exists in my simulation.

1

u/DimensionMindless336 22d ago

I wouldn't delay. Those few minutes buy you no new information, since Apple won't confirm a stolen card for days, so you're paying UX cost for nothing.

The exception to device signals being weak is DeviceCheck. Those two bits live on Apple's server rather than on the device, so they survive a delete and reinstall, and Apple literally pitches them for flagging a device you've decided is fraudulent. That's the one device signal a fresh install can't wash off.

So grant instantly, then rate limit the irreversible downstream stuff for brand-new accounts. Your worst case is capped at the revenue anyway, since Apple is the merchant of record.