Skip to content
Tolinku
Tolinku
Sign In Start Free
Use Cases · · 5 min read

Payment Deep Links: Direct Users to Payment Flows

By Tolinku Staff
|
Tolinku e commerce deep linking dashboard screenshot for use cases blog posts

Payment deep links route users directly to a specific payment flow in your fintech or banking app: send money to a recipient, pay a bill, split an expense, or complete a pending transaction. Instead of opening the app and navigating through menus, the user taps one link and lands on the payment screen with details pre-filled.

For the broader fintech deep linking approach, see Deep Linking for Fintech and Banking Apps. For P2P-specific patterns, see P2P Transfer Deep Links.

Person making a mobile payment with smartphone and credit card Photo by Leeloo The First on Pexels

Request Money

A user generates a payment request link that they share with the payer:

https://go.yourapp.com/pay/request?to=user_jane&amount=25.00&note=Lunch

When the payer taps this link:

  • App opens to a "Send Payment" screen
  • Recipient is pre-filled (Jane)
  • Amount is pre-filled ($25.00)
  • Note is pre-filled ("Lunch")
  • User confirms and sends

Bill Payment

A biller sends a deep link for payment:

https://go.yourapp.com/pay/bill?biller=ELECTRIC_CO&account=1234567890&amount=142.50&due=2026-05-15

The link pre-fills the bill payment form with the correct biller, account, amount, and due date.

Invoice Payment

For business invoices:

https://go.yourapp.com/pay/invoice/INV-2026-0042?amount=500.00

Split Payment

For splitting expenses among friends:

https://go.yourapp.com/split/SPLIT-ABC?your_share=18.75&note=Dinner+at+Sushi+Place

Pending Transaction Completion

When a transaction requires additional action (e.g., 2FA confirmation):

https://go.yourapp.com/transactions/TXN-12345/confirm

Implementation

URL Design

Keep payment deep links clean and human-readable:

Base: https://go.yourapp.com/pay
Request: /pay/request?to={recipient}&amount={amount}&note={note}
Bill:    /pay/bill?biller={code}&amount={amount}
Invoice: /pay/invoice/{id}
Split:   /split/{id}

App-Side Handler

function handlePaymentDeepLink(url) {
  const parsed = new URL(url);
  const path = parsed.pathname;
  const params = Object.fromEntries(parsed.searchParams);

  // Require authentication first
  if (user.isAuthenticated === false) {
    pendingDeepLink.save(url);
    navigation.navigate('Login');
    return;
  }

  if (path.startsWith('/pay/request')) {
    navigation.navigate('SendPayment', {
      recipient: params.to,
      amount: parseFloat(params.amount),
      note: params.note,
    });
  } else if (path.startsWith('/pay/bill')) {
    navigation.navigate('BillPayment', {
      billerCode: params.biller,
      accountNumber: params.account,
      amount: parseFloat(params.amount),
      dueDate: params.due,
    });
  } else if (path.startsWith('/pay/invoice')) {
    const invoiceId = path.split('/').pop();
    navigation.navigate('InvoicePayment', { invoiceId });
  } else if (path.startsWith('/split/')) {
    const splitId = path.split('/').pop();
    navigation.navigate('SplitPayment', { splitId });
  }
}

Security Requirements

Payment deep links handle money. Security is non-negotiable.

Authentication Before Payment

Never execute a payment from a deep link without authentication. The deep link pre-fills the payment form, but the user must:

  1. Be authenticated (biometrics, PIN, or password)
  2. Review the payment details
  3. Explicitly confirm the transaction
// Always require auth before showing payment screen
if (user.isAuthenticated === false) {
  // Save deep link for after auth
  storage.set('pending_payment_link', url);
  navigation.navigate('BiometricAuth');
  return;
}

Input Validation

Never trust data from the URL. Validate every parameter:

function validatePaymentParams(params) {
  // Amount validation
  const amount = parseFloat(params.amount);
  if (isNaN(amount) || amount <= 0 || amount > MAX_TRANSACTION_AMOUNT) {
    throw new Error('Invalid amount');
  }

  // Recipient validation
  if (params.to && isValidRecipient(params.to) === false) {
    throw new Error('Invalid recipient');
  }

  // Biller validation
  if (params.biller && isRegisteredBiller(params.biller) === false) {
    throw new Error('Unknown biller');
  }

  return { amount, recipient: params.to, biller: params.biller };
}

Rate Limiting

Prevent abuse by rate-limiting payment deep link processing:

  • Maximum 10 payment deep links per user per hour
  • Log all payment deep link attempts for audit

Signed URLs

For high-security payment links (invoices, bill pay from billers), use signed URLs:

https://go.yourapp.com/pay/invoice/INV-2026-0042?sig=hmac_sha256_signature&exp=1717027200

The app verifies:

  1. The signature matches the URL parameters (using a shared secret)
  2. The expiration timestamp hasn't passed

This prevents tampering (changing the amount or recipient) and replay attacks.

HTTPS Only

Payment deep links must use HTTPS Universal Links/App Links. Never use custom URL schemes (yourapp://pay/...) for payment flows because:

  • Custom schemes can be hijacked by malicious apps
  • Custom schemes don't verify domain ownership
  • Custom schemes aren't encrypted in transit

For deep link security best practices, see Deep Linking Security: Preventing Hijacking and Abuse.

User Experience Patterns

Confirmation Screen

Always show a confirmation screen before processing the payment:

╔═══════════════════════════════════╗
║  Send Payment                      ║
║                                    ║
║  To: Jane Smith                    ║
║  Amount: $25.00                    ║
║  Note: Lunch                       ║
║                                    ║
║  [Cancel]    [Confirm & Send]      ║
╚═══════════════════════════════════╝

The user must explicitly tap "Confirm & Send." The deep link populates the form; the user authorizes the action.

Editable Pre-Fill

Let users modify pre-filled values. The recipient might want to change the amount or add a different note:

<PaymentForm
  initialRecipient={deepLinkData.recipient}
  initialAmount={deepLinkData.amount}
  initialNote={deepLinkData.note}
  editable={true}
/>

Error States

Handle these gracefully:

  • Insufficient funds: Show balance and amount, offer to fund the account
  • Unknown recipient: Show "We couldn't find this person. Check the details."
  • Expired link: Show "This payment request has expired."
  • Already paid: Show "This payment was already completed."

Distribution Patterns

QR Code at Point of Sale

A merchant displays a QR code that deep links to a payment screen:

https://go.yourapp.com/pay/merchant/MERCH-001?amount=42.50&ref=TXN-789

The customer scans, sees "Pay $42.50 to Coffee Shop," and confirms.

Invoice Email

<a href="https://go.yourapp.com/pay/invoice/INV-2026-0042?amount=500&sig=...">
  Pay $500.00 →
</a>

Chat Message

Users share payment request links in messaging apps:

"You owe me $18.75 for dinner! Here's my payment link:
https://go.yourapp.com/pay/request?to=jane&amount=18.75¬e=Dinner"

Measuring Performance

Metric What It Measures
Deep link to auth rate % of payment link opens that authenticate
Auth to confirmation rate % of authenticated sessions that reach confirmation
Confirmation to completion rate % of confirmations that successfully transact
Average transaction value Mean payment amount from deep links
Error rate % of payment deep links that result in errors
Time to complete Seconds from link tap to transaction confirmed

For deep linking features, see Tolinku deep linking. For passing data through links, see Deep Link Parameters.

Get deep linking tips in your inbox

One email per week. No spam.

Ready to add deep linking to your app?

Set up Universal Links, App Links, deferred deep linking, and analytics in minutes. Free to start.