{"templateId":"markdown","sharedDataIds":{"sidebar":"sidebar-docs/sidebars.yaml"},"props":{"metadata":{"markdoc":{"tagList":[]},"type":"markdown"},"seo":{"title":"Integration guide for Web POS app","llmstxt":{"hide":false,"sections":[{"title":"Table of contents","includeFiles":["**/*"],"excludeFiles":[]}],"excludeFiles":[]}},"dynamicMarkdocComponents":[],"compilationErrors":[],"ast":{"$$mdtype":"Tag","name":"article","attributes":{},"children":[{"$$mdtype":"Tag","name":"Heading","attributes":{"level":1,"id":"integration-guide-for-web-pos-app","__idx":0},"children":["Integration guide for Web POS app"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Integrate Tap to Pay payments with your POS Android app using the Tyro Tap to Pay app."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"prerequisites","__idx":1},"children":["Prerequisites"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Your Web POS app running on Android"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Tyro Tap to Pay app installed on the merchant's device"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["A valid Tyro POS client ID (provided by Tyro)"]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"how-web-pos-integration-works","__idx":2},"children":["How Web POS integration works"]},{"$$mdtype":"Tag","name":"ol","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["The merchant opens your Web POS in the Android browser."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["The user taps ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Pay"]}," in your interface."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Your POS opens a secure link that launches the Tap to Pay app."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["The Tap to Pay app presents the payment screen, ready for the customer to tap their card."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["The Tap to Pay app processes the payment and closes, returning focus to the Web POS."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Your Web POS awaits the transaction result from either a webhook or polling, then displays the result."]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"important-request-outcomes-are-asynchronous","__idx":3},"children":["Important: Request outcomes are asynchronous"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["A Web POS running in Chrome cannot receive native app callbacks directly. Transaction outcomes are delivered via webhooks or direct polling — see ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"#receiving-transaction-results"},"children":["Receiving transaction results"]},"."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"implement-merchant-authorisation","__idx":4},"children":["Implement merchant authorisation"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Only supported in production. These are the steps for a merchant to authorise a POS to receive transaction results for Tap to Pay:"]},{"$$mdtype":"Tag","name":"ol","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Your POS generates and sends the merchant the unique Tyro Integration Portal URL, for example ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["https://integrate.tyro.com/embedded-payments?posId=[client_id]&posReference=[pos_reference]"]},"."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["The merchant signs into the Tyro Integration Portal."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["The merchant authorises the POS for every MID that will use Tap to Pay."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Your POS receives or retrieves confirmation."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Your POS can access transaction results from the authorised MID(s)."]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["This is the same Integration Portal mechanism used for ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/docs/in-person-payments/tap-to-pay/integration-guide/sdk/account-authorisation"},"children":["account authorisation on the SDK path"]}," — the merchant authorises your POS for a MID once, regardless of which integration model you're using."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"url-structure","__idx":5},"children":["URL structure"]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"launch-tap-to-pay-with-an-https-link","__idx":6},"children":["Launch Tap to Pay with an HTTPS link"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"header":{"controls":{"copy":{}}},"source":"https://auth.tyro.com/taptopay?action=<action>&type=<type>&data=<base64url-json>\n"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Parameters:"]}]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["action"]}," — what to do: ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["auth"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["transaction"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["pos"]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["type"]}," — a qualifier: ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["pair"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["request"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["info"]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["data"]}," — base64url-encoded JSON payload (RFC 4648 §5, URL-safe, ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["no padding"]},")"]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Example (decoded for clarity):"]}]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"header":{"controls":{"copy":{}}},"source":"action=transaction\ntype=request\ndata = Base64url({\n  \"type\": \"PURCHASE\",\n  \"amount\": 2999,\n  \"reference\": \"ORDER-12345\",\n  \"posInfo\": {\n    \"name\": \"WebPOS Terminal\",\n    \"vendor\": \"Acme Inc\",\n    \"version\": \"2.1.0\",\n    \"siteReference\": \"SITE-001\"\n  }\n})\n"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"supported-actions","__idx":7},"children":["Supported actions"]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"1-app-pairing","__idx":8},"children":["1. App Pairing"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Use this once to establish trust between your Web POS and the Tap to Pay app."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"header":{"controls":{"copy":{}}},"source":"https://auth.tyro.com/taptopay?action=auth&type=pair&data=Base64url({\"posClientId\":\"YOUR-POS-CLIENT-ID\"})\n"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Payload:"]}]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"json","header":{"controls":{"copy":{}}},"source":"{\n  \"posClientId\": \"YOUR-POS-CLIENT-ID\"\n}\n","lang":"json"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["or you can also pass the default POSInfo at this time"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"json","header":{"controls":{"copy":{}}},"source":"{\n  \"posClientId\": \"YOUR-POS-CLIENT-ID\",\n  \"posInfo\": {\n    \"name\": \"My WebPOS Name\",\n    \"vendor\": \"My Company\",\n    \"version\": \"1.0.0\",\n    \"siteReference\": \"LOCATION-001\"\n  }\n}\n","lang":"json"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["What happens:"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Tap to Pay shows a login screen."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["The merchant logs in with their Tyro account and configure the merchant ID (MID) and location as required."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Trust is established; future transactions run without login prompts."]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["NOTE"]}," Additional calls to Pair are ignored and simple close the Tap to Pay app and return to the last foregrounded app."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"2-run-a-transaction","__idx":9},"children":["2. Run a transaction"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Use this to process payments."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"header":{"controls":{"copy":{}}},"source":"https://auth.tyro.com/taptopay?action=transaction&type=request&data=Base64url({...})\n"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Payload:"]}]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"json","header":{"controls":{"copy":{}}},"source":"{\n  \"type\": \"PURCHASE\",\n  \"amount\": 5000,\n  \"reference\": \"ORDER-67890\",\n  \"posInfo\": {\n    \"name\": \"My WebPOS\",\n    \"vendor\": \"My Company\",\n    \"version\": \"1.0.0\",\n    \"siteReference\": \"LOCATION-001\"\n  }\n}\n","lang":"json"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Fields:"]}]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["type"]}," — ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["\"PURCHASE\""]}," or ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["\"REFUND\""]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["amount"]}," — amount in cents (e.g. 5000 = $50.00)"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["reference"]}," — unique order/transaction ID"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["posInfo"]}," — terminal information (optional if already set during ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"#1-app-pairing"},"children":["Pairing"]}," or ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"#3-register-pos-info"},"children":["Register POS info"]},")"]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Result:"]}]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Tap to Pay opens, and the merchant taps the customer's card."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["The transaction completes or fails."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["The user returns to your Web POS (or stays in Tap to Pay if they close it)."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Your backend receives the transaction result via Tyro's payment notification API."]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"3-register-pos-info","__idx":10},"children":["3. Register POS info"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Set the terminal details in Tap to Pay."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"header":{"controls":{"copy":{}}},"source":"https://auth.tyro.com/taptopay?action=pos&type=info&data=Base64url({...})\n"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Payload:"]}]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"json","header":{"controls":{"copy":{}}},"source":"{\n  \"name\": \"Register 3\",\n  \"vendor\": \"Acme POS v2.1\",\n  \"version\": \"2.1.0\",\n  \"siteReference\": \"STORE-456\"\n}\n","lang":"json"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"implementation-example-javascript","__idx":11},"children":["Implementation example (JavaScript)"]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"helper-base64url-encoding","__idx":12},"children":["Helper: base64url encoding"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"javascript","header":{"controls":{"copy":{}}},"source":"function toBase64Url(obj) {\n  const json = JSON.stringify(obj);\n  const bytes = new TextEncoder().encode(json);\n  let binary = String.fromCharCode(...bytes);\n  return btoa(binary)\n    .replace(/\\+/g, '-')\n    .replace(/\\//g, '_')\n    .replace(/=+$/, '');\n}\n","lang":"javascript"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"launch-a-transaction","__idx":13},"children":["Launch a transaction"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"javascript","header":{"controls":{"copy":{}}},"source":"function launchPayment(amountCents, reference) {\n  const payload = {\n    type: \"PURCHASE\",\n    amount: amountCents,\n    reference: reference,\n    posInfo: {\n      name: \"WebPOS Terminal\",\n      vendor: \"My Company\",\n      version: \"1.0.0\",\n      siteReference: \"SITE-001\"\n    }\n  };\n\n  const data = toBase64Url(payload);\n  const url = `https://auth.tyro.com/taptopay?action=transaction&type=request&data=${data}`;\n\n  // Open in new tab or same window (depends on your UX)\n  window.location.href = url;\n  // OR: window.open(url, '_blank');\n}\n\n// Call from your Pay button\ndocument.getElementById('payButton').addEventListener('click', () => {\n  launchPayment(2999, 'ORDER-12345');\n});\n","lang":"javascript"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"pair-once-on-setup","__idx":14},"children":["Pair once (on setup)"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"javascript","header":{"controls":{"copy":{}}},"source":"function pairPos(posClientId, posInfo) {\n  const payload = { posClientId: posClientId, posInfo: posInfo };\n  const data = toBase64Url(payload);\n  const url = `https://auth.tyro.com/taptopay?action=auth&type=pair&data=${data}`;\n\n  window.location.href = url;\n}\n","lang":"javascript"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"receiving-transaction-results","__idx":15},"children":["Receiving transaction results"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Since a Web POS doesn't get direct callbacks, here's how to handle results:"]},{"$$mdtype":"Tag","name":"ol","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Your Web POS opens the Tap to Pay deeplink when ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Pay"]},"ing on device."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["The card holder completes the transaction in Tap to Pay."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Tap to Pay submits the transaction to Tyro."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["(Optional) Tyro sends a payment notification to your backend via webhook; If webhook are configured."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Your backend updates the order status."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Your Web POS polls or listens for webhooks from your backend."]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Your backend receives the full transaction status and detail via Tyro's payment notification API — see the ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/api-explorer/tap-to-pay/1.0/notification-events"},"children":["Tap to Pay notification events reference"]},"."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"best-practices","__idx":16},"children":["Best practices"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["1. Pairing is a mandatory first on a new device."]}," Before processing transactions, run the pairing flow once. This establishes trust and allows faster transactions later."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["**2. (Optional) Set the POS info during pairing or register POS info"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["3. Validate amounts."]}," Ensure the amount is in cents (e.g. $25.99 = 2599):"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"javascript","header":{"controls":{"copy":{}}},"source":"const dollars = 25.99;\nconst cents = Math.round(dollars * 100);  // 2599\n","lang":"javascript"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["4. Use unique references."]}," Always include a unique ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["reference"]}," (e.g. order ID) so you can resolve transactions results:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"javascript","header":{"controls":{"copy":{}}},"source":"const reference = `MY_UNIQUE_TRNASACTION_REQUEST_ID`;\n","lang":"javascript"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["5. Include POS info."]}," If no default is set and no posInfo is passed with a transaction request, then Tap to Pay will be reject the transaction:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"javascript","header":{"controls":{"copy":{}}},"source":"const posInfo = {\n  name: `Register ${terminalId}`,\n  vendor: \"My POS Software\",\n  version: appVersion,\n  siteReference: locationId\n};\n","lang":"javascript"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["6. Handle a missing Tap to Pay app."]}," If Tap to Pay isn't installed, the user is taking to a 404 page."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["7. No action needed for deep link verification."]}," Tap to Pay links are verified via Android App Links (Digital Asset Links), which prevents unauthorised apps from intercepting requests — just use the correct domain, ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["auth.tyro.com"]},"."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"testing","__idx":17},"children":["Testing"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["All external testing uses the same Tap to Pay app, via ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["https://auth.tyro.com/taptopay?..."]},". Environments, test cards and simulator mode are covered in ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/docs/in-person-payments/tap-to-pay/testing#tap-to-pay-app"},"children":["Testing"]},"."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"troubleshooting","__idx":18},"children":["Troubleshooting"]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"tap-to-pay-is-not-installed","__idx":19},"children":["\"Tap to Pay is not installed\""]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Solution:"]}," the user needs to install Tyro Tap to Pay from the Play Store."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"invalid-data-or-malformed-json","__idx":20},"children":["\"Invalid data\" or \"Malformed JSON\""]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Possible causes:"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Base64url encoding is incorrect (ensure no padding, and use ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["-"]}," and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["_"]},")."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["The JSON isn't valid (check for missing commas or quotes)."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Required fields are missing."]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Debug tip:"]}," decode your ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["data"]}," parameter to verify the JSON:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"javascript","header":{"controls":{"copy":{}}},"source":"function decodeBase64Url(str) {\n  // Add padding if needed\n  const padded = str + '='.repeat((4 - str.length % 4) % 4);\n  const bytes = Uint8Array.from(atob(padded.replace(/-/g, '+').replace(/_/g, '/')), c => c.charCodeAt(0));\n  return new TextDecoder().decode(bytes);\n}\n","lang":"javascript"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"posclientid-mismatch-or-pos-client-id-not-paired","__idx":21},"children":["\"posClientId mismatch\" or \"POS client ID not paired\""]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Ensure you've run the pairing flow with the correct POS client ID."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Verify the POS client ID matches what Tyro assigned to your account."]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"transaction-timeout","__idx":22},"children":["\"Transaction timeout\""]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Possible causes: network connection lost during authorisation, NFC reader unavailable or malfunctioning, or the card removed during read."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Solution:"]}," retry the transaction."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"no-result-from-transaction","__idx":23},"children":["\"No result from transaction\""]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Expected behaviour:"]}," a Web POS doesn't receive the result directly. Your backend receives it via Tyro's payment notification API."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"security-notes","__idx":24},"children":["Security notes"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["HTTPS only."]}," All Tyro Tap to Pay links use ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["https://auth.tyro.com"]},". Never use ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["http://"]},"."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["App link verification."]}," The ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["auth.tyro.com"]}," domain is verified via Digital Asset Links, which prevents man-in-the-middle attacks and rogue apps intercepting payment intents. You don't need to do anything — just use the correct domain."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["POS client ID."]}," Treat your ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["posClientId"]}," like an API key. Don't hard-code it in client-side code if possible; retrieve it from your backend."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["User privacy."]}," Transaction data is sent to Tap to Pay, which handles it securely. Your Web POS shouldn't log or store sensitive details."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"support-and-next-steps","__idx":25},"children":["Support and next steps"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Contact Tyro to receive your unique ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["posClientId"]}," for production use, and to have your account set up in UAT with test credentials. Work with Tyro's integration team to configure payment notification webhooks for your backend, set up transaction reconciliation, and enable any advanced features such as refunds or digital receipts."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"appendix-full-url-examples","__idx":26},"children":["Appendix: full URL examples"]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"pairing-base64url-shown-decoded-for-clarity","__idx":27},"children":["Pairing (base64url shown decoded for clarity)"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"header":{"controls":{"copy":{}}},"source":"https://auth.tyro.com/taptopay?action=auth&type=pair&data=eyJwb3NDaWVudElkIjoiTVktUE9TLUlEIn0\n"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Decoded ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["data"]},": ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["{\"posClientId\":\"MY-POS-ID\"}"]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"transaction","__idx":28},"children":["Transaction"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"header":{"controls":{"copy":{}}},"source":"https://auth.tyro.com/taptopay?action=transaction&type=request&data=eyJ0eXBlIjoiUFVSQ0hBU0UiLCJhbW91bnQiOjI5OTksInJlZmVyZW5jZSI6Ik9SREVSLTI0NjciLCJwb3NJbmZvIjp7Im5hbWUiOiJXZWJQT1MiLCJ2ZW5kb3IiOiJNeUNvIiwidmVyc2lvbiI6IjEuMC4wIiwic2l0ZVJlZmVyZW5jZSI6IlNJVEUtMDAxIn19\n"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Decoded ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["data"]},":"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"json","header":{"controls":{"copy":{}}},"source":"{\n  \"type\": \"PURCHASE\",\n  \"amount\": 2999,\n  \"reference\": \"ORDER-2467\",\n  \"posInfo\": {\n    \"name\": \"WebPOS\",\n    \"vendor\": \"MyCo\",\n    \"version\": \"1.0.0\",\n    \"siteReference\": \"SITE-001\"\n  }\n}\n","lang":"json"},"children":[]}]},"headings":[{"value":"Integration guide for Web POS app","id":"integration-guide-for-web-pos-app","depth":1},{"value":"Prerequisites","id":"prerequisites","depth":2},{"value":"How Web POS integration works","id":"how-web-pos-integration-works","depth":2},{"value":"Important: Request outcomes are asynchronous","id":"important-request-outcomes-are-asynchronous","depth":3},{"value":"Implement merchant authorisation","id":"implement-merchant-authorisation","depth":2},{"value":"URL structure","id":"url-structure","depth":2},{"value":"Launch Tap to Pay with an HTTPS link","id":"launch-tap-to-pay-with-an-https-link","depth":3},{"value":"Supported actions","id":"supported-actions","depth":2},{"value":"1. App Pairing","id":"1-app-pairing","depth":3},{"value":"2. Run a transaction","id":"2-run-a-transaction","depth":3},{"value":"3. Register POS info","id":"3-register-pos-info","depth":3},{"value":"Implementation example (JavaScript)","id":"implementation-example-javascript","depth":2},{"value":"Helper: base64url encoding","id":"helper-base64url-encoding","depth":3},{"value":"Launch a transaction","id":"launch-a-transaction","depth":3},{"value":"Pair once (on setup)","id":"pair-once-on-setup","depth":3},{"value":"Receiving transaction results","id":"receiving-transaction-results","depth":2},{"value":"Best practices","id":"best-practices","depth":2},{"value":"Testing","id":"testing","depth":2},{"value":"Troubleshooting","id":"troubleshooting","depth":2},{"value":"\"Tap to Pay is not installed\"","id":"tap-to-pay-is-not-installed","depth":3},{"value":"\"Invalid data\" or \"Malformed JSON\"","id":"invalid-data-or-malformed-json","depth":3},{"value":"\"posClientId mismatch\" or \"POS client ID not paired\"","id":"posclientid-mismatch-or-pos-client-id-not-paired","depth":3},{"value":"\"Transaction timeout\"","id":"transaction-timeout","depth":3},{"value":"\"No result from transaction\"","id":"no-result-from-transaction","depth":3},{"value":"Security notes","id":"security-notes","depth":2},{"value":"Support and next steps","id":"support-and-next-steps","depth":2},{"value":"Appendix: full URL examples","id":"appendix-full-url-examples","depth":2},{"value":"Pairing (base64url shown decoded for clarity)","id":"pairing-base64url-shown-decoded-for-clarity","depth":3},{"value":"Transaction","id":"transaction","depth":3}],"frontmatter":{"title":"Integration guide for Web POS app","seo":{"title":"Integration guide for Web POS app"}},"lastModified":"2026-09-23T03:06:33.000Z","pagePropGetterError":{"message":"","name":""}},"slug":"/docs/in-person-payments/tap-to-pay/integration-guide/web-app","userData":{"isAuthenticated":false,"teams":["anonymous"]},"isPublic":true}