# PayPal In-app

**Useful links**

* Billing Agreement API\
  [PayPal Initiate billing agreement](/online-payments/advanced-payment-methods-apms/paypal/your-first-transaction-with-paypal/paypal-initiate-billing-agreement.md)

## Merchant setup <a href="#paypalinappmerchantreferenceimplementationdocumentation-merchantsetup" id="paypalinappmerchantreferenceimplementationdocumentation-merchantsetup"></a>

This section describes the possible merchant setups and highlights crucial data for in-app transaction processing.

At the core of the structure is the *merchant*, who owns at least one *store*. Merchants and stores are represented by abstract entities. Usually, the merchant will configure one bank account at the merchant entity to receive the settlement. However, it is possible to configure a separate bank account at each store as well. The settlement reports are in turn tied to the bank account(s), since they contain the breakdown of the received bank statement.

To enable the processing of in-app transactions, each store needs a *Payment Provider Contract* (PPC) linked to it, which is uniquely defined by the *contract UUID*. Each PPC contains a reference to the *bank account* which will be used for settlement of transactions processed with this contract.

To ensure proper reporting, the merchant (or the franchise) needs to maintain a mapping of the stores and PPC UUIDs, since the later one is needed during transaction processing. The same PPC UUID needs to be used for processing refunds, which alternatively can be processed from terminals connected to the store where the transaction was processed.

<div data-with-frame="true"><figure><img src="/files/vaPnUXhX9PwFhEFOP0a3" alt=""><figcaption></figcaption></figure></div>

In case the merchant is part of a franchise structure, there will be an additional corporation level in the hierarchy, below which the corporate owned merchant structure and any number of franchisee merchant structures are located.

<div data-with-frame="true"><figure><img src="/files/sYIm7B5f6YFDo5jdp88O" alt=""><figcaption></figcaption></figure></div>

## User management flows <a href="#paypalinappmerchantreferenceimplementationdocumentation-flowsfrommerchantperspective" id="paypalinappmerchantreferenceimplementationdocumentation-flowsfrommerchantperspective"></a>

It is assumed that the merchant already has an existing user management including, e.g., account creation and login inside the merchant app.

### Billing agreement management

A billing agreement is needed to process in-app transactions. It is best practice to manage the billing agreement at the merchant backend and not store any sensitive data inside the merchant app.

### Create Billing Agreement

The creation of the billing agreement is initiated by the user within the merchant app, e.g., during account setup, account configuration or before the first PayPal transaction.

Therefore, the merchant backend needs to **initiate** the billing agreement to receive the *approvalUrl* and the *billingToken*, which is a temporary reference until the final *agreementId* is created.

The user needs to be **redirected** to the *approvalUrl* to log into his/her PayPal account and **confirm** the billing agreement.

Once the user got **redirected** after the confirmation, the final step is to **create** the billing agreement and receive the *agreementId*, which needs to be **saved** on the merchant backend and linked to the user account.

<div data-with-frame="true"><img src="https://verifone.cloud/sites/default/files/inline-images/Billing_Agreement_creation_2021-06-25.png" alt="" height="852" width="785"></div>

### Cancel Billing Agreement

The merchant app needs to provide an option to **cancel** the billing agreement linked to the user account.

The merchant backend then collects the required data to perform the **cancel** request towards the Verifone backend.

Once the successful response is received, the *agreementId* is deleted from the user account and the same is confirmed to the user inside the app.

<div data-with-frame="true"><img src="https://verifone.cloud/sites/default/files/inline-images/Billing_Agreement_cancellation_2021-06-25.png" alt="" height="428" width="729"></div>

## Transaction processing with Billing Agreement <a href="#paypalinappmerchantreferenceimplementationdocumentation-transactionprocessingwithbillingagreement" id="paypalinappmerchantreferenceimplementationdocumentation-transactionprocessingwithbillingagreement"></a>

### PayPal risk management

PayPal requires the integration of their **Magnes SDK**, which is a portion of PayPal's fraud and risk management. From the Magnes SDK description:

> *With direct access to the mobile application, Magnes accesses necessary information from the device and provides the data to PayPal Risk Services for early risk identification and mitigation. Magnes is implemented in the form of a library available for the Android and iOS platforms.*

The high-level steps to integrate the Magnes SDK in PayPal transaction processing are shown below:

<div data-with-frame="true"><figure><img src="/files/YyHBaBuDREkPXuK0HpvP" alt=""><figcaption></figcaption></figure></div>

Further implementation details can be found in the PayPal Magnes SDK documentation.

### Transaction processing

<div data-with-frame="true"><img src="https://verifone.cloud/sites/default/files/inline-images/Transaction_processing_with_Billing_Agreement_2021-06-25.png" alt="" height="957" width="877"></div>

## Reference merchant app implementation <a href="#paypalinappmerchantreferenceimplementationdocumentation-referencemerchantappimplementation" id="paypalinappmerchantreferenceimplementationdocumentation-referencemerchantappimplementation"></a>

This section shows sample screens of the app, showcasing above flows for Billing Agreement management and transaction processing. Some code samples are provided as well.

**Sample screens**

The following sample screens show a transaction with seamlessly integrated creation of billing agreement:

<table data-header-hidden><thead><tr><th valign="top"></th><th valign="top"></th><th valign="top"></th><th valign="top"></th><th valign="top"></th></tr></thead><tbody><tr><td valign="top"><img src="https://verifone.cloud/sites/default/files/inline-images/01-login.png" alt="1. User login" data-size="original"></td><td valign="top"><img src="https://verifone.cloud/sites/default/files/inline-images/02-home.png" alt="2. Home" data-size="original"></td><td valign="top"><img src="https://verifone.cloud/sites/default/files/inline-images/03-add-to-basket.png" alt="3. Add item to basket" data-size="original"></td><td valign="top"><img src="https://verifone.cloud/sites/default/files/inline-images/04-basket.png" alt="4. Basket" data-size="original"></td><td valign="top"><img src="https://verifone.cloud/sites/default/files/inline-images/05-payment.png" alt="5. Checkout" data-size="original"></td></tr><tr><td valign="top"><ol><li>User login</li></ol></td><td valign="top"><ol start="2"><li>Home</li></ol></td><td valign="top"><ol start="3"><li>Add item to basket</li></ol></td><td valign="top"><ol start="4"><li>Basket</li></ol></td><td valign="top"><ol start="5"><li>Checkout</li></ol></td></tr><tr><td valign="top"><img src="https://verifone.cloud/sites/default/files/inline-images/06-paypal-login.png" alt="6. PayPal login*" data-size="original"></td><td valign="top"><img src="https://verifone.cloud/sites/default/files/inline-images/07-paypal-agreement.png" alt="7. Billing Agreement confirmation*" data-size="original"></td><td valign="top"><img src="https://verifone.cloud/sites/default/files/inline-images/08-paypal-thanks.png" alt="8. Successful Billing Agreement*" data-size="original"></td><td valign="top"><img src="https://verifone.cloud/sites/default/files/inline-images/09-place-order.png" alt="9. Place order" data-size="original"></td><td valign="top"><img src="https://verifone.cloud/sites/default/files/inline-images/10-payment-success.png" alt="10. Payment result" data-size="original"></td></tr><tr><td valign="top"><ol start="6"><li>PayPal login*</li></ol></td><td valign="top"><ol start="7"><li>Billing Agreement confirmation*</li></ol></td><td valign="top"><ol start="8"><li>Successful Billing Agreement*</li></ol></td><td valign="top"><ol start="9"><li>Place order</li></ol></td><td valign="top"><ol start="10"><li>Payment result</li></ol></td></tr></tbody></table>

\*Steps 6, 7 and 8 are only needed when the first transaction is being processed. With a billing agreement in place, the flow jumps from 5 to 9 (e.g., when the second transaction is being processed).

### Platforms for app development

**App development options and limitations**

There are several possibilities to develop an app to be run on Android and iOS:

* separate development for each platform
  * Android
    * programming language: Kotlin, Java
    * UI: traditional imperative, declarative (Jetpack Compose)
    * communication libs: okhttp, ktor
  * iOS
    * Swift, Objective C
    * UIKit, SwiftUI
* cross platform development
  * progressive web apps: HTML/CSS, React, Angular, Vue
  * hybrid: PhoneGap, Cordova
  * compile to native: Xamarin, NativeScript, React Native, Flutter, Kotlin multiplatform mobile

Only *compile to native* solutions create real apps found in-app stores and are running fast.

Solutions based on JavaScript (e.g., React Native) need additional wrappers for native libraries to be used (e.g., the required PayPal Magnes SDK). This is also true for Xamarin using C#.

Using Kotlin multiplatform mobile, it is necessary to develop UI twice; only non-UI code can be reused.

**Development approach used for the reference merchant app implementation**

It is not possible to find a solution, where the code can be reused by all possible integrators without change. To reduce effort, cross platform development has been chosen.

Using **Flutter** (<https://flutter.dev/>, <https://en.wikipedia.org/wiki/Flutter_(software)>), there are none of the above limitations. That is why it has been chosen as platform for the example merchant app. It is using **Dart** (<https://dart.dev/>, <https://en.wikipedia.org/wiki/Dart_(programming_language)>) as programming language. Thus, examples below are in this language (or GraphQL).

**Communication to server**

Beside OAuth2 for user login, GraphQL ([https://graphql.org](https://graphql.org/)) is used for communication with merchant server. It is a query language for APIs similar to REST.

App is using Dart package GraphQL client (<https://pub.dev/packages/graphql>).

### Billing Agreement management

**Create Billing Agreement**

App is sending the following GraphQL mutation for creation of billing agreement:

**mutation CreateBillingAgreement**

```php
mutation CreateBillingAgreement($paymentOptionId: ID, $successUrl: String!, $cancelUrl: String!) {
  createBillingAgreement(paymentOptionId: $paymentOptionId, successUrl: $successUrl, cancelUrl: $cancelUrl) {
    id,
    url,
    state
  }
}
```

Parameters of mutation (successUrl, cancelUrl) are sent to PayPal to be used as return url after processing of PayPal website. In case of success, server returns a billing agreement (id, url, state).

An optional payment option can be specified.

**Seamless confirmation of Billing Agreement**

App launches web browser with *url* returned by server. Flutter plugin url\_launcher (<https://pub.dev/packages/url_launcher>) is used:

**launch url**

```java
BillingAgreement agreement = await createBillingAgreement();
if ((agreement != null) && (await canLaunch(agreement.url))) {
  await setAgreementId(agreement.id);
  await launch(agreement.url);
}
```

To return to the app at the receipt of *url* (deep link), the app must be configured according to operating system.

**Android**

Application manifest must include an intent filter for activity:

**intent filter for deep link**

```xml
<meta-data android:name="flutter_deeplinking_enabled" android:value="true" />
<intent-filter android:autoVerify="true">
  <action android:name="android.intent.action.VIEW" />
  <category android:name="android.intent.category.DEFAULT" />
  <category android:name="android.intent.category.BROWSABLE" />
  <data android:scheme="verishop" />
</intent-filter>
```

**iOS**

In an iOS application, the same is done by changing Info.plist:

**Info.plist**

```xml
<key>CFBundleURLTypes</key>
<array>
  <dict>
    <key>CFBundleTypeRole</key>
    <string>Editor</string>
    <key>CFBundleURLName</key>
    <string>routes</string>
    <key>CFBundleURLSchemes</key>
    <array>
      <string>verishop</string>
    </array>
  </dict>
</array>
<key>FlutterDeepLinkingEnabled</key>
<true/>
```

Back in the app, routing must be set according to received return url. Using Flutter, this is done independent from OS. onGenerateRoute is used in build function of app main widget.

**flutter routing**

```java
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    ...
    onGenerateRoute: (settings) {
      final uri = Uri.parse(settings.name);
      if (uri.pathSegments.length == 2 && uri.pathSegments.first == 'agreement') {
        final action = uri.pathSegments[1];
        if (action == 'success' || action == 'cancel') {
          return MaterialPageRoute(builder: (context) => DashboardScreen(action: action));
        }
        if (action == 'transaction') {
          return MaterialPageRoute(builder: (context) => PlaceOrderScreen(action: action));
        }
        ...
      }
      ...
    }
  }
}
```

In this example, return url 'verishop\://agreement/success' and 'verishop\://agreement/cancel' are redirected to DashboardScreen. In case of successful billing agreement during transaction (url 'verishop\://agreement/transaction'), it is routed to PlaceOrderScreen.

A successful billing agreement must be activated using following GraphQL mutation:

**mutation ActivateBillingAgreement**

```php
mutation ActivateBillingAgreement($id: String) {
  activateBillingAgreement(id: $id) {
    id,
    state
  }
}
```

In case of success, the server returns the state 'ACTIVE'.

**Cancel Billing Agreement**

The app is sending the following GraphQL mutation for cancellation of billing agreement:

**mutation CancelBillingAgreement**

```php
mutation CancelBillingAgreement($id: String) {
  cancelBillingAgreement(id: $id) {
    id,
    state
  }
}
```

In case of success, the server returns the state 'CANCELLED'.

**Set Billing Agreement as default payment option**

Billing agreements are stored on server for the user, as payment option (name, agreement). The option is returned to the app at user login and can be used during transaction without creating agreement again.

### Transaction flow

**Create Basket**

On home screen, the app shows a list of items with title, description, picture, price, and tax.

**items.dart**

```php
class SaleItem {
  String title;
  String description;
  String picture;
  int price;
  int tax;
  SaleItem(this.title, this.description, this.picture, this.price, this.tax);
  String getFormattedPrice([int quantity = 1]) => formatAmount(price * quantity);
}
 
List _items = [];
```

The item list is sent by server to the app.

Using Flutter for each item, a widget is built as one row of a list:

**create row for item**

```java
Widget _buildRow(BuildContext context, int i) {
  final item = getSaleItem(i);
  return Padding(
    padding: const EdgeInsets.only(top: 10.0, bottom: 10.0),
    child: ListTile(
      leading: Icon(context, 'assets/images/${item.picture}'),
      title: Text('${item.title}', style: _biggerFont),
      subtitle: Text('${item.description}', style: _smallerFont),
      trailing: ElevatedButton(
        child: Text('\$ ${item.getFormattedPrice()}'),
        onPressed: () => _add2basket(context, item))));
}
```

Additional to picture, title, and description, it shows a button with price. Pressing this button adds the item to the basket.

The basket screen shows a list of basket items and total amount. Additionally, it redirects to checkout page, if the corresponding button is pressed.

**Checkout screen**

This screen shows payment option(s) returned by server. For each item in the list, an icon (if image exists in assets), name (e.g., PayPal) and checkbox are shown.

**show payment option(s)**

```java
Widget _optionRow(BuildContext context, AppState model, int i) {
  final optionName = model.options[i].name;
  final imageFile = 'assets/images/${optionName.toLowerCase()}.png';
  return ListTile(
    title: Text('$optionName', style: biggerFont),
    leading: icon(context, imageFile),
    trailing: Checkbox(
      onChanged: (value) {
        setState(() {
          _activeOption = optionName;
        });
      },
      value: _activeOption == optionName,
    ),
  );
}
```

Active option is stored in the estate variable \_activeOption. Corresponding checkbox is checked. In this case, no billing agreement must be created during transaction.

If no checkbox is checked, the user can select it. In this case, billing agreement will be created if the user presses **Continue** before switching to the "place order" screen.

**select payment option**

```java
ElevatedButton(
  onPressed: model.isAgreementActive(_activeOption)
    ? () => Navigator.push(context, MaterialPageRoute(
        builder: (context) => PlaceOrderScreen(),
      ))
    : () => createAgreement(context, true, model.getOptionByName(_activeOption).id),
  child: Text('Continue'))
```

**"Place order" screen**

This screen shows transaction details (subtotal, tax, total, selected payment option, basket items) again before starting transaction via pressing **place order**.

In this case, the following GraphQL mutation is sent to merchant server:

**transaction**

```php
mutation transaction($agreementId: ID!, $totalAmount: AmountInput!, $items: [ItemInput], $fraudId: ID) {
  transaction(agreementId: $agreementId, totalAmount: $totalAmount, items: $items, fraudId: $fraudId) {
    id,
    state
  }
}
```

fraudId is used for PayPal Magnes SDK, see below.

After processing, the app switches to payment result screen.

**Payment result**

This screen shows transaction result (success or error) and some transaction details.

Additionally, there is a button that redirects to Home Screen.

In case of successful transaction, the basket is cleared.

### PayPal Magnes SDK

Magnes implements functions (setUp, collect, collectAndSubmit), which are used by merchant app:

* setUp is called once at startup of application
* collectAndSubmit is called during transaction. It returns the PayPal-Client-Metadata-Id which is sent as fraudId to the merchant server.

While SDK can be used directly for pure Android or iOS apps, using a platform independent framework makes it necessary to implement an abstraction layer. The solution for Flutter is based on messages, see <https://flutter.dev/docs/development/platform-integration/platform-channels?tab=ios-channel-swift-tab>. The following example is using MethodChannel for Dart part of Magnes setup:

**Magnes setup**

```java
const platform = const MethodChannel('verishop.verifone.com/magnes');
 
Future<bool> magnesSetup() async {
  bool result = false;
  try {
    result = await platform.invokeMethod('setup');
  } on PlatformException catch (e) {
    print('Failed to setup magnes: ${e.message}');
  }
  return result;
}
```

## Reference merchant backend implementation <a href="#paypalinappmerchantreferenceimplementationdocumentation-referencemerchantback-endimplementation" id="paypalinappmerchantreferenceimplementationdocumentation-referencemerchantback-endimplementation"></a>

Merchant server is implemented using following technologies:

* programming language TypeScript
* node.js with express framework
* Mongo DB
* GraphQL
* OAuth2

### eComm API

This documentation does not describe all details of implementation. The main focus is on the description of integration for PayPal Ecomm API provided by Verifone.

Some TypeScript code fragments using node.js express and bent are provided as examples.

### Billing agreement

**Creation**

To create a new billing agreement, send a POST to the endpoint '/billingAgreement/initiate'. The Parameters of payload are payment provider contract id (*ppcId)* and URLs for **return** and **cancel**. An additional authorization header for **basic authentication** is necessary.

```javascript
const post = bent('POST', 'json', 201, {
  authorization: paymentOption.auth
}, paymentOption.baseUrl)
const obj = await post('/billingAgreement/initiate', {
  paymentProviderContract: paymentOption.ppcId,
  returnUrl: successUrl,
  cancelUrl: cancelUrl
})
```

A successful result will include *approvalUrl* and *billingToken*.&#x20;

**Activation**

The endpoint for activation of an agreement is '/billingAgreement/create'. The payload should include parameters *ppcId* and *billingToken* received on creation.

```javascript
const post = bent('POST', 'json', 201, {
  authorization: option.auth
}, option.baseUrl)
const obj = await post('/billingAgreement/create', {
  paymentProviderContract: option.ppcId,
  billingToken: agreement.token,
})
```

The result message includes parameter state which should be 'ACTIVE'.

**Cancellation**

The endpoint for cancellation is '/billingAgreement/$id/cancel'. *ppcId* is necessary as parameter in the payload.

```javascript
const post = bent('POST', 'buffer', 200, {
  authorization: option?.auth
}, option.baseUrl)
await post(`/billingAgreement/${agreement.agreementId}/cancel`, {
  paymentProviderContract: option.ppcId
})
```

### Transaction

Messages for transactions should use the '/transactions' endpoint. The payload should include *ppcId*, *billingAgreementId* and a transaction object (amount, items, etc.). Additional headers `x-vfi-api-idempotencyKey` and `paypalFraudId` are necessary.

**transaction request**

```javascript
const headers: Record<string, string> = {
  authorization: option.auth,
  'x-vfi-api-idempotencyKey': uuidV4(),
  paypalFraudId: fraudId
}
const post = bent('POST', 'json', 201, headers, option.baseUrl)
const initObj = await post('/transactions', {
  paymentProviderContract: option.ppcId,
  billingAgreementId: agreement.agreementId,
  ...transObj
})
```

**Example of an API used between merchant app and backend**

The complete API of the merchant server as GraphQL schema:

```php
type Query {
  merchant: Merchant!
  merchants: [Merchant]
  user: User!
  users: [User]
}
 
type Mutation {
  createBillingAgreement(paymentOptionId: ID, successUrl: String!, cancelUrl: String!): BillingAgreement!
  activateBillingAgreement(id: ID!): BillingAgreement!
  cancelBillingAgreement(id: ID!): BillingAgreement!
  transaction(agreementId: ID!, totalAmount: AmountInput!, items: [ItemInput], fraudId: ID): Transaction!
  createUser(merchantId: ID, name: String!, password: String!): User!
  createMerchant(name: String!): Merchant!
  createPaymentOption(merchantId: ID, name: String!, baseUrl: String, auth: String, ppcId: String): PaymentOption!
  createSaleItem(merchantId: ID, title: String!, description: String, picture: String, price: Int!, tax: Int): SaleItem!
  removeSaleItem(title: String!): Boolean!
}
 
input AmountInput {
  value: Int!
  currency: String!
}
 
input ItemInput {
  name: String!
  unitAmount: AmountInput!
  taxAmount: AmountInput!
  quantity: Int
}
 
type Amount {
  value: Int!
  currency: String!
}
 
type Item {
  name: String!
  unitAmount: Amount!
  taxAmount: Amount!
  quantity: Int
}
 
enum State {
  PENDING,
  ACTIVE,
  CANCELLED
}
 
scalar Date
 
type PaymentOption {
  id: ID!
  name: String!
  billingAgreement: BillingAgreement
}
 
type BillingAgreement {
  url: String
  state: State
  id: ID!
}
 
type Transaction {
  id: ID!
  created: Date!
  state: String
  totalAmount: Amount!
  items: [Item]
}
 
type Merchant {
  id: ID!
  name: String!
  users: [User]
  saleItems: [SaleItem]
}
 
type SaleItem {
  title: String!
  description: String
  picture: String
  price: Int!
  tax: Int!
}
 
type User {
  id: ID!
  name: String!
  paymentOptions: [PaymentOption]
  transactions: [Transaction]
}
```

Besides queries (one or all merchants or users), it includes mutations used by the merchant app: create, activate and cancel billing agreement, transaction. Other mutations are used for server administration: create user, create merchant, create payment option, create, and remove sale item.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.verifone.com/online-payments/advanced-payment-methods-apms/paypal/your-first-transaction-with-paypal/paypal-in-app.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
