Skip to content

Migrate payments to in-app events ​

Move from capturePayment to trackEvent, which sends each payment as an in-app event with revenue

In the new SDK versions, a payment is an in-app event with revenue. You send it with trackEvent, the same method you use for custom events.

capturePayment and removePayment are deprecated. They still work in the new versions and show a deprecation warning in your IDE, and they will be removed in a future major version. Apps on older SDK versions keep working with no change. Move to trackEvent when you upgrade.

Use the exact same event name, with the same spelling and letter case.

Your old payment type becomes the event name. If you sent FIRST_PAYMENT before, send "FIRST_PAYMENT" now. Do not change it to first_payment, First_Payment, or firstPayment.

Linkrunner matches the name exactly. A different spelling is treated as a new in-app event, so your existing Meta and Google mappings will not match it, and those payments will not be sent to Meta or Google.

Migrate with your AI coding agent ​

If you use Claude Code, Cursor, GitHub Copilot, or Windsurf, install the migration skill in your app's repo:

bash
npx @linkrunner/skills add migrate-payments

Then ask your agent:

text
Migrate my Linkrunner capturePayment and removePayment calls to trackEvent.

The agent finds every payment call, upgrades the SDK to the right version, keeps each payment name exactly as before, and asks you for the currency of your payments. Review its changes and test one payment on a debug build, as in How to migrate. See Linkrunner Agent Skills for setup.

Before and after ​

The old capturePayment call, then the same payment with trackEvent. The product keys are optional: Meta receives content_ids, contents, content_type, num_items, and order_id, and Google receives content_ids as item IDs. amount stays the order total.

Before (Android SDK 4.x):

kotlin
LinkRunner.getInstance().capturePayment(
    CapturePaymentRequest(
        paymentId = "bk_10231",
        userId = "playo_u_8842",
        amount = 1200.0,
        type = PaymentType.FIRST_PAYMENT,
        status = PaymentStatus.PAYMENT_COMPLETED
    )
)

After (Android SDK 5.0.0):

kotlin
// Payment: same trackEvent, payment fields in eventData
LinkRunner.getInstance().trackEvent(
    eventName = "FIRST_PAYMENT", // was type, same spelling
    eventData = mapOf(
        "amount" to 1200.0,
        "paymentId" to "bk_10231",
        "currency" to "INR", // new, required
        "userId" to "playo_u_8842",
        "status" to "PAYMENT_COMPLETED", // optional, the default
        // product details for Meta and Google
        "content_ids" to listOf("court_c2_1800"),
        "contents" to listOf(mapOf("id" to "court_c2_1800", "quantity" to 1, "item_price" to 1200.0)),
        "content_type" to "product",
        "num_items" to 1,
        "order_id" to "bk_10231"
    )
)

From Java, call LinkrunnerJava.getInstance().trackEvent("FIRST_PAYMENT", eventData, null, callback) with the same keys. See Tracking Payments.

How to migrate ​

For each capturePayment call, make three edits:

  1. Rename capturePayment to trackEvent.
  2. Pass your old type as the event name, the first argument, with exactly the same spelling.
  3. Move the payment fields into the event data: amount, paymentId, userId, and status keep their names. Add currency, which is new and required, for example "USD" or "INR".

Then replace removePayment calls as shown in Refunds and removePayment, and test once with a debug build. The payment should show on the Events page under the same name as before.

capturePaymenttrackEvent
typethe event name (first argument), same value and spelling
amountamount in the event data, the order total
paymentIdpaymentId in the event data, required
userIduserId in the event data, still required. An empty string uses the user saved by signup or setUserData
statusstatus in the event data, optional, defaults to PAYMENT_COMPLETED
eventDatathe other keys in the event data, such as product details
Not availablecurrency in the event data, new and required

The event is a payment only when the event data has amount, paymentId, and currency together. If any of the three is missing, it is not a payment:

  • amount or currency without paymentId is a normal custom event, exactly as before.
  • paymentId with amount or currency missing is an error, and nothing is sent.

Only PAYMENT_COMPLETED payments count as revenue and are sent to ad networks. See Payment status for the other statuses.

Old payment types ​

These are the old payment types. Send them exactly like this, in uppercase:

FIRST_PAYMENT, SECOND_PAYMENT, TRIAL_STARTED, TRIAL_CANCELLED, WALLET_TOPUP, FUNDS_WITHDRAWAL, SUBSCRIPTION_CREATED, SUBSCRIPTION_RENEWED, SUBSCRIPTION_CANCELLED, SUBSCRIPTION_REFUNDED, SUBSCRIPTION_BILLING_ISSUE, DEFAULT, ONE_TIME, RECURRING

If you did not pass a type before, your payments were saved as DEFAULT. Use "DEFAULT" as the event name. In Flutter, PaymentType.DEFAULT_PAYMENT was also saved as DEFAULT, so use "DEFAULT", not "DEFAULT_PAYMENT".

SDK versions ​

PlatformPackageOlder versionsNew versions (trackEvent payments, capturePayment deprecated)
Androidio.linkrunner:android-sdk4.x5.0.0
iOSLinkrunnerKit4.x5.0.0
React Nativern-linkrunner3.x4.0.0
Flutterlinkrunner4.x5.0.0
Expoexpo-linkrunner3.x4.0.0, with rn-linkrunner 4.0.0 or later
Capacitorcapacitor-linkrunner2.x3.0.0
Cordovacordova-linkrunner1.x2.0.0
UnityUnity bridgesBridges on native 4.xBridges on native 5.0.0

Older SDK versions ​

Linkrunner also records a payment sent with trackEvent from an older SDK, so payments are not lost while your users update the app. The event data must have paymentId, amount, currency, and userId, spelled exactly like that.

Older SDKs send the event as it is and do not check it on the device, so upgrade when you can:

  • If a key is missing or invalid, the event is stored as a custom event. The request still succeeds and your app sees no error. New SDK versions reject the call instead.
  • amount must be a number of 0 or more, and currency a 3-letter ISO 4217 code.
  • status is optional and must be exactly PAYMENT_INITIATED, PAYMENT_COMPLETED, PAYMENT_FAILED, or PAYMENT_CANCELLED.
  • payment_id in snake case is not read inside the event data.

Send each payment through one path only. Use capturePayment or trackEvent for a payment, not both. The same paymentId sent through both under the same name is stored once. Under different names it counts as two payments, so your revenue is counted twice.

Refunds and removePayment ​

removePayment is deprecated. It still works in the new versions and shows a deprecation warning, and it will be removed in a future major version. To record a refund or a cancelled booking, call trackEvent with the same event name, paymentId, amount, and currency as the original payment, and status set to PAYMENT_CANCELLED. All three payment fields are needed, or the SDK rejects the call. That payment no longer counts as revenue, and nothing is sent to ad networks.

Using a new event name ​

You can use a new name such as purchase instead of an old type. Before you release, add a mapping for that name in the dashboard for Meta, Google, and any other partner that should get it. Without a mapping, the payment is saved in Linkrunner but not sent to ad networks.

Keep one name per payment. The same paymentId sent under two names counts as two payments.

Sending payments from your server ​

Use POST /api/v1/capture-event with amount, payment_id, and currency next to event_name and user_id. The same naming rule applies. See Events Capture API. POST /api/v1/capture-payment is deprecated but keeps working.

Need help? Contact support@linkrunner.io.