> ## Documentation Index
> Fetch the complete documentation index at: https://integration.wpay.com.au/llms.txt
> Use this file to discover all available pages before exploring further.

# Paze on mobile

> Add Paze to native iOS and Android apps using the Paze mobile SDK and the Gr4vy server SDK.

This guide shows how to add Paze to a native iOS or Android app. The shopper completes the Paze flow in your app via the Paze mobile SDK, and your server uses the Gr4vy server SDK to orchestrate the session and create the resulting transaction.

For background on how Paze works and how Gr4vy fits in, read the [overview](/guides/features/paze/overview).

A runnable web simulation of this flow is available in the [`sample-standalone-paze`](https://github.com/gr4vy/sample-standalone-paze) repository, alongside the web version. The repository shows the server-side calls end-to-end against the Gr4vy API; pair it with the Paze iOS or Android SDK in your own app for the native launch step.

## Before you begin

Each merchant that wants to use Paze needs to be approved by Early Warning Services (EWS), and Gr4vy then enables Paze on a per-merchant basis. To start onboarding, see [Set up Paze](/connections/digital-wallets/paze#set-up-paze) on the connector page.

The native Paze iOS and Android SDKs are distributed by Paze. Your Paze onboarding contact provides the SDK, the integration documentation, and the credentials your app needs.

<Warning>
  The server-side examples assume you have already created a Gr4vy client authenticated with your private API key. See [Authentication](/guides/api/authentication) to create the client. Keep your API key and the Gr4vy client on the server — never expose them in your mobile app.
</Warning>

## Overview of the flow

The mobile integration has a server side and an app side that coordinate through a callback URL:

1. **Server-side** — create a Paze mobile session with Gr4vy. The session returns the Paze merchant data `id`, `name`, `profileId`, an `accessToken`, and a `sessionId`.
2. **Server-side** — create a Paze checkout URL with the session values, your callback URL scheme, and the intent.
3. **App-side** — launch the Paze checkout URL using the Paze iOS or Android SDK. After the shopper finishes, Paze invokes your callback URL scheme with an opaque response code.
4. **App-side** — forward the response code to your server.
5. **Server-side** — (optional) review the session. Required for `REVIEW_AND_PAY` if you want Gr4vy to add shipping details to the transaction; skipped for `EXPRESS_CHECKOUT`.
6. **Server-side** — complete the session to get the opaque token.
7. **Server-side** — create a Gr4vy transaction with the token.

## Step 1: Create a Paze mobile session

On your server, create a Paze session with `source` set to `"mobile"`. Unlike the web flow, `domain_name` is not required for mobile sessions. The response includes the merchant `id`, `name`, `profileId`, plus the `accessToken` and `sessionId` you pass to the next step.

<CodeGroup>
  ```csharp C# theme={"system"}
  var res = await client.DigitalWallets.Sessions.PazeAsync(
      pazeSessionRequest: new PazeSessionRequest()
      {
          Source = "mobile",
      }
  );

  PazeMobileSession? session = null;
  if (res?.Type == ResponseCreatePazeDigitalWalletSessionType.PazeMobileSession)
  {
      session = res.PazeMobileSession;
  }
  ```

  ```go Go theme={"system"}
  source := components.SourceMobile

  res, err := client.DigitalWallets.Sessions.Paze(ctx, components.PazeSessionRequest{
      Source: &source,
  })
  if err != nil {
      log.Fatal(err)
  }

  var session *components.PazeMobileSession
  if res != nil && res.Type == operations.ResponseCreatePazeDigitalWalletSessionTypePazeMobileSession {
      session = res.PazeMobileSession
  }
  ```

  ```java Java theme={"system"}
  CreatePazeDigitalWalletSessionResponse res = gr4vyClient.digitalWallets().sessions().paze()
      .pazeSessionRequest(PazeSessionRequest.builder()
          .source(Source.MOBILE)
          .build())
      .call();

  PazeMobileSession session = null;
  if (res.responseCreatePazeDigitalWalletSession().isPresent()) {
      Object raw = res.responseCreatePazeDigitalWalletSession().get().value();
      if (raw instanceof PazeMobileSession) {
          session = (PazeMobileSession) raw;
      }
  }
  ```

  ```php PHP theme={"system"}
  $pazeSessionRequest = new PazeSessionRequest(source: 'mobile');
  $response = $sdk->digitalWallets->sessions->paze(pazeSessionRequest: $pazeSessionRequest);
  $session = $response->responseCreatePazeDigitalWalletSession;
  ```

  ```python Python theme={"system"}
  session = client.digital_wallets.sessions.paze(
      source="mobile",
  )
  ```

  ```ts TypeScript theme={"system"}
  const session = await gr4vy.digitalWallets.sessions.paze({
    source: "mobile",
  });
  ```
</CodeGroup>

<Warning>
  Use the same `accessToken` and `sessionId` for every server call in this checkout (create, review, complete). Do not reuse them for a different checkout — request a new session each time.
</Warning>

```json Response theme={"system"}
{
  "id": "W8GT9RLCNME754Z7025613H3PDM2T4HF2CSAOi9w2kkP3D4S0",
  "name": "ACME",
  "profileId": "550e8400-e29b-41d4-a716-446655440000",
  "accessToken": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
  "sessionId": "7c1cba03-d20e-4a3f-9d77-e5dc23a39ac2"
}
```

## Step 2: Create the Paze checkout URL

Call `paze_mobile_session_create` with the values from Step 1, the callback URL scheme your app handles, the intent, and the transaction value. The response includes a `pazeCheckoutUrl` to open in the Paze SDK.

The supported intents are:

* `REVIEW_AND_PAY` — the shopper reviews shipping and card details before paying. Your app and server then call the review step before completing.
* `EXPRESS_CHECKOUT` — the shopper pays immediately without a review step.

<CodeGroup>
  ```csharp C# theme={"system"}
  var create = await client.DigitalWallets.Sessions.PazeMobileSessionCreateAsync(
      pazeMobileSessionCreateRequest: new PazeMobileSessionCreateRequest()
      {
          Client = new PazeClient() { Id = session.Id, Name = session.Name, ProfileId = session.ProfileId },
          SessionId = session.SessionId,
          AccessToken = session.AccessToken,
          CallbackURLScheme = "myapp://paze/return",
          Intent = "REVIEW_AND_PAY",
          TransactionValue = new PazeTransactionValue()
          {
              TransactionAmount = "50.21",
              TransactionCurrency = "USD",
          },
          TransactionType = "PURCHASE",
      }
  );
  ```

  ```go Go theme={"system"}
  create, err := client.DigitalWallets.Sessions.PazeMobileSessionCreate(ctx, components.PazeMobileSessionCreateRequest{
      Client: components.PazeClient{
          ID:        session.ID,
          Name:      &session.Name,
          ProfileID: &session.ProfileID,
      },
      SessionID:         session.SessionID,
      AccessToken:       session.AccessToken,
      CallbackURLScheme: "myapp://paze/return",
      Intent:            "REVIEW_AND_PAY",
      TransactionValue: &components.PazeTransactionValue{
          TransactionAmount:   "50.21",
          TransactionCurrency: "USD",
      },
      TransactionType: gr4vy.String("PURCHASE"),
  })
  ```

  ```java Java theme={"system"}
  CreatePazeMobileSessionResponse createRes = gr4vyClient.digitalWallets().sessions().pazeMobileSessionCreate()
      .pazeMobileSessionCreateRequest(PazeMobileSessionCreateRequest.builder()
          .client(PazeClient.builder()
              .id(session.id())
              .name(session.name())
              .profileId(session.profileId())
              .build())
          .sessionId(session.sessionId())
          .accessToken(session.accessToken())
          .callbackURLScheme("myapp://paze/return")
          .intent("REVIEW_AND_PAY")
          .transactionValue(PazeTransactionValue.builder()
              .transactionAmount("50.21")
              .transactionCurrency("USD")
              .build())
          .transactionType("PURCHASE")
          .build())
      .call();

  PazeMobileSessionCreate create = createRes.pazeMobileSessionCreate().orElse(null);
  ```

  ```php PHP theme={"system"}
  $create = $sdk->digitalWallets->sessions->pazeMobileSessionCreate(
      pazeMobileSessionCreateRequest: new PazeMobileSessionCreateRequest(
          client: new PazeClient(
              id: $session->id,
              name: $session->name,
              profileId: $session->profileId,
          ),
          sessionId: $session->sessionId,
          accessToken: $session->accessToken,
          callbackURLScheme: 'myapp://paze/return',
          intent: 'REVIEW_AND_PAY',
          transactionValue: new PazeTransactionValue(
              transactionAmount: '50.21',
              transactionCurrency: 'USD',
          ),
          transactionType: 'PURCHASE',
      )
  );
  ```

  ```python Python theme={"system"}
  create = client.digital_wallets.sessions.paze_mobile_session_create(
      client={
          "id": session.id,
          "name": session.name,
          "profile_id": session.profile_id,
      },
      session_id=session.session_id,
      access_token=session.access_token,
      callback_url_scheme="myapp://paze/return",
      intent="REVIEW_AND_PAY",
      transaction_value={
          "transaction_amount": "50.21",
          "transaction_currency": "USD",
      },
      transaction_type="PURCHASE",
  )
  ```

  ```ts TypeScript theme={"system"}
  const create = await gr4vy.digitalWallets.sessions.pazeMobileSessionCreate({
    client: { id: session.id, name: session.name, profileId: session.profileId },
    sessionId: session.sessionId,
    accessToken: session.accessToken,
    callbackURLScheme: "myapp://paze/return",
    intent: "REVIEW_AND_PAY",
    transactionValue: {
      transactionAmount: "50.21",
      transactionCurrency: "USD",
    },
    transactionType: "PURCHASE",
  });
  ```
</CodeGroup>

Return `create.pazeCheckoutUrl`, `session.sessionId`, and `session.accessToken` to your app, along with the chosen `intent`. Your app needs all three to handle the callback and call your server again in Step 4.

## Step 3: Launch Paze in your app

Use the Paze iOS or Android SDK to open `pazeCheckoutUrl`. The exact native call is specific to each platform — refer to the SDK documentation your Paze onboarding contact provides.

When the shopper finishes, Paze invokes your callback URL scheme (the one you passed in Step 2) with a `response` parameter containing a base64-encoded JWE. Your app decodes that value and sends it to your server, together with the `sessionId`, `accessToken`, and `intent` from Step 2.

See [`sample-standalone-paze`](https://github.com/gr4vy/sample-standalone-paze) for an end-to-end reference of the callback decode and the server-side review/complete/transaction calls.

<Warning>
  The callback `response` is sensitive material that authorizes the transaction. Forward it directly to your server over a trusted channel — do not persist it client-side or send it to other destinations.
</Warning>

## Step 4: Review the session

This step is optional. Call `paze_mobile_session_review` when you want Gr4vy to add the shipping address (and the rest of the review data) to the transaction in Step 6. Skip it for `EXPRESS_CHECKOUT`, or for `REVIEW_AND_PAY` checkouts where you do not need those details — and pass the original callback `code` directly to Step 5.

Call `paze_mobile_session_review` with the JWE `code` from the callback. The response includes the selected card (masked), the consumer, and the shipping address. When you call review, use `review.code` as the input to Step 5.

<CodeGroup>
  ```csharp C# theme={"system"}
  var review = await client.DigitalWallets.Sessions.PazeMobileSessionReviewAsync(
      pazeSessionReviewRequest: new PazeSessionReviewRequest()
      {
          SessionId = sessionId,
          Code = code,
          AccessToken = accessToken,
      }
  );
  ```

  ```go Go theme={"system"}
  review, err := client.DigitalWallets.Sessions.PazeMobileSessionReview(ctx, components.PazeSessionReviewRequest{
      SessionID:   sessionId,
      Code:        code,
      AccessToken: accessToken,
  })
  ```

  ```java Java theme={"system"}
  ReviewPazeMobileSessionResponse reviewRes = gr4vyClient.digitalWallets().sessions().pazeMobileSessionReview()
      .pazeSessionReviewRequest(PazeSessionReviewRequest.builder()
          .sessionId(sessionId)
          .code(code)
          .accessToken(accessToken)
          .build())
      .call();

  PazeSessionReview review = reviewRes.pazeSessionReview().orElse(null);
  ```

  ```php PHP theme={"system"}
  $review = $sdk->digitalWallets->sessions->pazeMobileSessionReview(
      pazeSessionReviewRequest: new PazeSessionReviewRequest(
          sessionId: $sessionId,
          code: $code,
          accessToken: $accessToken,
      )
  );
  ```

  ```python Python theme={"system"}
  review = client.digital_wallets.sessions.paze_mobile_session_review(
      session_id=session_id,
      code=code,
      access_token=access_token,
  )
  ```

  ```ts TypeScript theme={"system"}
  const review = await gr4vy.digitalWallets.sessions.pazeMobileSessionReview({
    sessionId,
    code,
    accessToken,
  });
  ```
</CodeGroup>

## Step 5: Complete the session

Call `paze_mobile_session_complete` with the code from the previous step (`review.code` for `REVIEW_AND_PAY`, or the original callback `code` for `EXPRESS_CHECKOUT`). The response includes `completeResponse`, the opaque token you forward to Gr4vy as the transaction token.

<CodeGroup>
  ```csharp C# theme={"system"}
  var complete = await client.DigitalWallets.Sessions.PazeMobileSessionCompleteAsync(
      pazeSessionCompleteRequest: new PazeSessionCompleteRequest()
      {
          SessionId = sessionId,
          Code = completeCode,
          AccessToken = accessToken,
          TransactionType = "PURCHASE",
      }
  );
  ```

  ```go Go theme={"system"}
  complete, err := client.DigitalWallets.Sessions.PazeMobileSessionComplete(ctx, components.PazeSessionCompleteRequest{
      SessionID:       sessionId,
      Code:            completeCode,
      AccessToken:     accessToken,
      TransactionType: "PURCHASE",
  })
  ```

  ```java Java theme={"system"}
  CompletePazeMobileSessionResponse completeRes = gr4vyClient.digitalWallets().sessions().pazeMobileSessionComplete()
      .pazeSessionCompleteRequest(PazeSessionCompleteRequest.builder()
          .sessionId(sessionId)
          .code(completeCode)
          .accessToken(accessToken)
          .transactionType("PURCHASE")
          .build())
      .call();

  PazeSessionComplete complete = completeRes.pazeSessionComplete().orElse(null);
  ```

  ```php PHP theme={"system"}
  $complete = $sdk->digitalWallets->sessions->pazeMobileSessionComplete(
      pazeSessionCompleteRequest: new PazeSessionCompleteRequest(
          sessionId: $sessionId,
          code: $completeCode,
          accessToken: $accessToken,
          transactionType: 'PURCHASE',
      )
  );
  ```

  ```python Python theme={"system"}
  complete = client.digital_wallets.sessions.paze_mobile_session_complete(
      session_id=session_id,
      code=complete_code,
      access_token=access_token,
      transaction_type="PURCHASE",
  )
  ```

  ```ts TypeScript theme={"system"}
  const complete = await gr4vy.digitalWallets.sessions.pazeMobileSessionComplete({
    sessionId,
    code: completeCode,
    accessToken,
    transactionType: "PURCHASE",
  });
  ```
</CodeGroup>

## Step 6: Create a transaction

Create the transaction with the opaque `completeResponse` from Step 5 as `token`. Set `checkout_token` to the **original** JWE callback `code` from Step 3 (not `review.code`) so Gr4vy can populate the masked card details and shipping address on the transaction.

<Warning>
  The Paze mobile SDK and `transactionValue` use decimal-string major units (for example `"50.21"`), while Gr4vy's transaction `amount` is an integer in the smallest currency unit (for example `5021` for \$50.21). Convert between the two when bridging the values, and make sure they match for the same purchase.
</Warning>

<CodeGroup>
  ```csharp C# theme={"system"}
  var transaction = await client.Transactions.CreateAsync(
      transactionCreate: new TransactionCreate()
      {
          Amount = 5021,
          Currency = "USD",
          Country = "US",
          PaymentMethod = TransactionCreatePaymentMethod.CreatePazePaymentMethodCreate(
              new PazePaymentMethodCreate()
              {
                  Method = "paze",
                  Token = complete.CompleteResponse,
                  CheckoutToken = code,
              }
          ),
      }
  );
  ```

  ```go Go theme={"system"}
  pazePaymentMethodCreate := components.PazePaymentMethodCreate{
      Method:        "paze",
      Token:         *complete.CompleteResponse,
      CheckoutToken: &code,
  }
  paymentMethod := components.CreateTransactionCreatePaymentMethodPazePaymentMethodCreate(pazePaymentMethodCreate)

  transaction, err := client.Transactions.Create(ctx, components.TransactionCreate{
      Amount:        5021,
      Currency:      "USD",
      Country:       gr4vy.String("US"),
      PaymentMethod: &paymentMethod,
  }, nil, nil)
  ```

  ```java Java theme={"system"}
  CreateTransactionResponse transactionResponse = gr4vyClient.transactions().create()
      .transactionCreate(TransactionCreate.builder()
          .amount(5021L)
          .currency("USD")
          .country("US")
          .paymentMethod(TransactionCreatePaymentMethod.of(PazePaymentMethodCreate.builder()
              .method("paze")
              .token(complete.completeResponse().orElseThrow())
              .checkoutToken(code)
              .build()))
          .build())
      .call();

  Transaction transaction = transactionResponse.transaction().orElse(null);
  ```

  ```php PHP theme={"system"}
  $transactionCreate = new TransactionCreate(
      amount: 5021,
      currency: 'USD',
      country: 'US',
      paymentMethod: new PazePaymentMethodCreate(
          method: 'paze',
          token: $complete->completeResponse,
          checkoutToken: $code,
      ),
  );
  $response = $sdk->transactions->create($transactionCreate);
  $transaction = $response->transaction;
  ```

  ```python Python theme={"system"}
  transaction = client.transactions.create(
      amount=5021,
      currency="USD",
      country="US",
      payment_method={
          "method": "paze",
          "token": complete.complete_response,
          "checkout_token": code,
      },
  )
  ```

  ```ts TypeScript theme={"system"}
  const transaction = await gr4vy.transactions.create({
    amount: 5021,
    currency: "USD",
    country: "US",
    paymentMethod: {
      method: "paze",
      token: complete.completeResponse,
      checkoutToken: code,
    },
  });
  ```
</CodeGroup>

The response is a standard Gr4vy transaction. Handle its `status` as you would for any other payment method.

## Test Paze

To test Paze on mobile, drive your integration in a simulator or device build with a Paze test wallet.

<Note>
  Test wallet credentials are issued by Paze, not by Gr4vy. To obtain a test wallet for your merchant, work with Paze directly through your Paze onboarding contact.
</Note>

For general sandbox environment information, see Paze's [Environments guide](https://developer.paze.com/design-sandbox/docs/environments).
