NAV

Mobile - App to App

Overview Demo Integration steps

Integration Steps

  1. End-users choose ZaloPay Payment method on the Merchant application.
  2. Merchant send request to ZaloPay (create order API). ZaloPay server return the order's information to Merchant-side.
  3. Merchant application will ask user to allow open ZaloPay app to processing payment, the SDK will open ZaloPay/Zalo app to proceed the payment
  4. If payment successfully, ZaloPay app auto relaunch Merchant app to display payment result and ZaloPay server will notify Merchant server about this
  5. Merchant server should  query for order's status  when haven't received ZaloPay's callback yet
  6. Merchant needs to handle  callback  from ZaloPay when End-User pay success.

Main process sequences

App to App Payment Flow

In case of ZaoPay is not installed

App to App Payment Flow

Download SDK AppToApp

iOS

SDK AppToApp Integration

SDK AppToApp was distributed as libraries. Project need to be integrated with ZPDK's API following the instructions below in other to use it.

  1. Add ZPDK.framework into project.
  2. Add configuration to allow app to open ZaloPay by adding  zalozalopay and zalopay.api.v2 into LSApplicationQueriesSchemes key
<key>LSApplicationQueriesSchemes</key>
<array>
    <string>zalo</string>
    <string>zalopay</string>
    <string>zalopay.api.v2</string>
</array>

ZPDK Initialize

In the AppDelegate.m, add calling request to ZPDK to processing data transaction between ZaloPay and App.

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
  [[ZaloPaySDK sharedInstance] initWithAppId:<appID> uriScheme:@"<uriScheme>" environment:<ZPZPIEnvironment>]; //ZPDK Initialize
}

- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options{
  return [[ZaloPaySDK sharedInstance] application:app openURL:url sourceApplication:@"vn.com.vng.zalopay" annotation:nil]; //Calling request to ZPDK to process data transaction 
}
  func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    ZaloPaySDK.sharedInstance()?.initWithAppId(<appid>, uriScheme: "<uriScheme>", environment: <ZPZPIEnvironment>)
    return true
  }

  func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
    return ZaloPaySDK.sharedInstance().application(app, open: url, sourceApplication:"vn.com.vng.zalopay", annotation: nil)
  }

Notice: Call this function because ZPDK need to check if sourceApplication is belong to ZaloPay

- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation
  func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {}

Call payment function

Call payment function to process

  [[ZaloPaySDK sharedInstance] initWithAppId:<appID> uriScheme:@"<uriScheme>" environment:<ZPZPIEnvironment>]; //Reinit ZPDK if you want to pay with another AppID
  [ZaloPaySDK sharedInstance].paymentDelegate = self; //Depend on where you handle ZPPaymentDelegate.
  [[ZaloPaySDK sharedInstance] payOrder:zpTransToken];
  ZaloPaySDK.sharedInstance()?.initWithAppId(<appid>, uriScheme: "<uriScheme>", environment: <ZPZPIEnvironment>) //Reinit ZPDK if you want to pay with another AppID
  ZaloPaySDK.sharedInstance()?.paymentDelegate = self  //Depend on where you handle ZPPaymentDelegate. 
  ZaloPaySDK.sharedInstance()?.payOrder(zpTransToken.text)

Process responses

  - (void)paymentDidSucceeded:(NSString *)transactionId zpTranstoken:(NSString *)zpTranstoken appTransId:(NSString *)appTransId {
    //Handle Success
  }
  - (void)paymentDidCanceled:(NSString *)zpTranstoken appTransId:(NSString *)appTransId {
    //Handle User Canceled
  }
  - (void)paymentDidError:(ZPPaymentErrorCode)errorCode zpTranstoken:(NSString *)zpTranstoken appTransId:(NSString *)appTransId {
    //Redirect to Zalo/ZaloPay Store when errorCode == ZPPaymentErrorCode.appNotInstall
    //Handle Error
  }
  func paymentDidSucceeded(_ transactionId: String!, zpTranstoken: String!, appTransId: String!) {
      //Handle Success
  }

  func paymentDidCanceled(_ zpTranstoken: String!, appTransId: String!) {
      //Handle User Canceled
  }

  func paymentDidError(_ errorCode: ZPPaymentErrorCode, zpTranstoken : String!, appTransId: String!) {
      //Redirect to Zalo/ZaloPay Store when errorCode == ZPPaymentErrorCode.appNotInstall
      //Handle Error
  }

Android

SDK AppToApp Integration

You have to following these intructions below to integrate ZPDK to Android Studio:

configurations.maybeCreate("default")
artifacts.add("default", file('zpdk-release.aar'))

ZPDK Initialize

Add ZPDK initializtion in Application's onCreate function

@Override
public void onCreate() {
  ...
  //ZPDK Initialize
  ZaloPaySDK.init(<appID>, <Environment>);
}
    override fun onCreate(savedInstanceState: Bundle?) {
      ..
      ZaloPaySDK.init(<appID>, Environment);
    }

Calling payment function from ZPDK and get callback

//Need to catch OnNewIntent event because ZaloPay App will call deeplink to Merchant's app
@Override
public void onNewIntent(Intent intent) {
  super.onNewIntent(intent);
  ZaloPaySDK.getInstance().onResult(intent);
}

//Reinit ZPDK if you want to pay with another AppID
ZaloPaySDK.tearDown();
ZaloPaySDK.init(<appID>, <Environment>);

//Payment function
ZaloPaySDK.getInstance().payOrder(
  <Activity>, <Token>, <YourAppUriScheme>, new MyZaloPayListener()
);

//Implement interface PayOrderListener to get payment result 
private static class MyZaloPayListener implements PayOrderListener {
  @Override
  public void onPaymentSucceeded(final String transactionId, final String transToken, final String appTransID) {
    //Handle Success
  }

  @Override
  public void onPaymentCanceled(String zpTransToken, String appTransID) {
    //Handle User Canceled
  }
  @Override
  public void onPaymentError(ZaloPayError zaloPayError, String zpTransToken, String appTransID) {
    //Redirect to Zalo/ZaloPay Store when zaloPayError == ZaloPayError.PAYMENT_APP_NOT_FOUND
    //Handle Error
  }
}
  //Need to catch OnNewIntent event because ZaloPay App will call deeplink to Merchant's app
  override fun onNewIntent(intent: Intent) {
    super.onNewIntent(intent)
    ZaloPaySDK.getInstance().onResult(intent)
  }

  //Reinit ZPDK if you want to pay with another AppID
  ZaloPaySDK.tearDown();
  ZaloPaySDK.init(<appID>, Environment);

  //Payment function
  ZaloPaySDK.getInstance().payOrder(<Activity>, <Token>!!, "<MerchantApp Deeplink>",object: PayOrderListener {
    override fun onPaymentCanceled(zpTransToken: String?, appTransID: String?) {
      //Handle User Canceled
    }
    override fun onPaymentError(zaloPayErrorCode: ZaloPayError?, zpTransToken: String?, appTransID: String?) {
      //Redirect to Zalo/ZaloPay Store when zaloPayError == ZaloPayError.PAYMENT_APP_NOT_FOUND
      //Handle Error
    }
    override fun onPaymentSucceeded(transactionId: String, transToken: String, appTransID: String?) {
      //Handle Success
    }
  })

React Native

ZPDK Integration and Initialization

iOS

Android

  1. Open android project in React Project by Android Studio.
  2. Following the same instructions Android above
  3. In the getPackages() function, add new PayZaloBridge() into Array.asList(...) function like:
@Override
protected List<ReactPackage> getPackages() {
  return Array.asList(
    ...,
    new PayZaloBridge()
  );
}

Call payment function

import {NativeModules, NativeEventEmitter} from 'react-native';
const { PayZaloBridge } = NativeModules;
const payZaloBridgeEmitter = new NativeEventEmitter(PayZaloBridge);

componentDidMount() {
  this.subscription = payZaloBridgeEmitter.addListener(
    'EventPayZalo',
    (data) => {
      if(data.returnCode == 1){
        alert('Payment successully!');
      } else{
        alert('Payment failed!');
      }
    }
  );
}

componentWillUnmount() {
  this.subscription.remove();
}

let payZP = NativeModules.PayZaloBridge;
payZP.payOrder(zptranstoken);

Flutter

ZPDK Integration and Initialization

iOS

Init MethodChannel to call ZaloPay Pay method

  static const MethodChannel platform = MethodChannel('flutter.native/channelPayOrder'); //Example of creating MethodChannel

  final String result = await platform.invokeMethod('payOrder', {"zptoken": zpToken}); //Example of calling payOrder

Init EventChannel to get payment result from ZaloPay App

static const EventChannel eventChannel = EventChannel('flutter.native/eventPayOrder');
  @override
  void initState() {
    super.initState();
    if (Platform.isIOS) {
      eventChannel.receiveBroadcastStream().listen(_onEvent, onError: _onError); //Listening for event
    }
  }

Send result to EventChannel

  private var eventSink: FlutterEventSink?

  func paymentDidSucceeded(_ transactionId: String!, zpTranstoken: String!, appTransId: String!) {
    //Example of Handling Success
    guard let eventSink = eventSink else {
      return
    }
    eventSink(["errorCode": 1, "zpTranstoken": zpTranstoken, "transactionId": transactionId, "appTransId": appTransId])
  }
  func paymentDidCanceled(_ zpTranstoken: String!, appTransId: String!) {
    //Example of Handling User Canceled
    guard let eventSink = eventSink else {
      return
    }
    eventSink(["errorCode": 4, "zpTranstoken": zpTranstoken, "appTransId": appTransId])
  }
  func paymentDidError(_ errorCode: ZPPaymentErrorCode, zpTranstoken: String!, appTransId: String!) {
    //Example of Handling Error
    guard let eventSink = eventSink else {
      return
    }
    //Redirect to Zalo/ZaloPay Store when errorCode == ZPPaymentErrorCode.appNotInstall
    eventSink(["errorCode": errorCode, "zpTranstoken": zpTranstoken, "appTransId": appTransId])
  }

Android

Use Method Channel to call payment function

MethodChannel(flutterEngine.dartExecutor.binaryMessenger, channelPayOrder)
  .setMethodCallHandler { call, result ->
    if (call.method == "payOrder"){
        val token = call.argument<String>("zptoken")
        ZaloPaySDK.getInstance().payOrder(this@MainActivity, token !!, "<UriScheme>",object: PayOrderListener {
            override fun onPaymentCanceled(zpTransToken: String?, appTransID: String?) {
              result.success("User Canceled")
            }
            override fun onPaymentError(zaloPayErrorCode: ZaloPayError?, zpTransToken: String?, appTransID: String?) {
              //Redirect to Zalo/ZaloPay Store when zaloPayError == ZaloPayError.PAYMENT_APP_NOT_FOUND
              result.success("Payment failed")
            }
            override fun onPaymentSucceeded(transactionId: String, transToken: String, appTransID: String?) {
              result.success("Payment Success")
            }
        })
    }
  }

ZPDK responses data

Transaction successfully - Handle function paymentDidSucceeded()

Parameter Datatype Description
transactionId string Transaction ID
zpTranstoken string ZPTranstoken from ZPDK send to ZaloPay to discern orders
appTransId string Order's transaction code

Transaction cancelled - Handle function paymentDidCanceled()

Parameter Datatype Description
zpTranstoken string ZPTranstoken from ZPDK send to ZaloPay to discern orders
appTransId string Order's transaction code

Transaction error - Handle function paymentDidError()

Parameter Datatype Description
errorCode number Error code
zpTranstoken string ZPTranstoken from ZPDK send to ZaloPay to discern orders
appTransId string Order's transaction code

Note: If user has not installed ZaloPay or the Zalo app, response error code: -1. Merchant creates navigation to App Store/Google Play for user to download app
Example

{
  if errorCode == ZPPaymentErrorCode.appNotInstall {

      ZaloPaySDK.sharedInstance()?.navigateToZaloStore();            // navigator to Zalo App

      // ZaloPaySDK.sharedInstance()?.navigateToZaloPayStore();   // navigator to ZaloPay App

      return;
  }
}
No matching results were found