Skip to content

Commit

Permalink
Android Example with Custom Tabs (#3)
Browse files Browse the repository at this point in the history
* Add android example

* Fix cancel url

* Update AndroidPayPalWebSdkIntegrationExample/app/src/main/java/com/example/androidpaypalwebsdkintegrationexample/CatalogFragment.java

Co-authored-by: Jax DesMarais-Leder <[email protected]>

* Update AndroidPayPalWebSdkIntegrationExample/app/src/main/java/com/example/androidpaypalwebsdkintegrationexample/CatalogFragment.java

Co-authored-by: Jax DesMarais-Leder <[email protected]>

* Update AndroidPayPalWebSdkIntegrationExample/app/src/main/java/com/example/androidpaypalwebsdkintegrationexample/ShoppingCartFragment.java

Co-authored-by: Tim Chow <[email protected]>

* Update AndroidPayPalWebSdkIntegrationExample/app/src/main/java/com/example/androidpaypalwebsdkintegrationexample/ShoppingCartFragment.java

Co-authored-by: Tim Chow <[email protected]>

* remove auto generated codes and extraneous super invocation

* Use liner layout instead of constrained layout and remove PayPal.com button

* Remove wrapping of single widgets inside layouts, use linear layout instead of app bar

* Use const for urls

---------

Co-authored-by: Jax DesMarais-Leder <[email protected]>
Co-authored-by: Tim Chow <[email protected]>
  • Loading branch information
3 people authored Sep 11, 2024
1 parent 5de8e4a commit dd75e30
Show file tree
Hide file tree
Showing 41 changed files with 1,130 additions and 0 deletions.
14 changes: 14 additions & 0 deletions AndroidPayPalWebSdkIntegrationExample/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
*.iml
.gradle
/local.properties
/.idea/
.DS_Store
/build
/captures
.externalNativeBuild
.cxx
local.properties
gradle/
*.jar
/build
/app/build
43 changes: 43 additions & 0 deletions AndroidPayPalWebSdkIntegrationExample/app/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
plugins {
id 'com.android.application'
}

android {
namespace 'com.example.androidpaypalwebsdkintegrationexample'
compileSdk 33

defaultConfig {
applicationId "com.example.androidpaypalwebsdkintegrationexample"
minSdk 33
targetSdk 33
versionCode 1
versionName "1.0"

testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}

buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt')
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_11
targetCompatibility JavaVersion.VERSION_11
}
buildFeatures {
viewBinding true
}
}

dependencies {

implementation 'androidx.appcompat:appcompat:1.3.0'
implementation 'com.google.android.material:material:1.8.0'
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
implementation 'androidx.navigation:navigation-fragment:2.5.3'
implementation 'androidx.navigation:navigation-ui:2.5.3'
implementation 'androidx.browser:browser:1.5.0'
implementation 'androidx.core:core:1.1.0'
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">

<uses-permission android:name="android.permission.INTERNET" />

<queries>
<intent>
<action android:name="android.support.customtabs.action.CustomTabsService" />
</intent>
</queries>

<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.AndroidPayPalWebSdkIntegrationExample"
tools:targetApi="31">
<activity
android:name=".OnPaymentCompletionActivity"
android:exported="true">
<tools:validation testUrl="https://shipping-change-sandbox-e66e294a9266.herokuapp.com/onsuccess" />
<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="https"
android:host="shipping-change-sandbox-e66e294a9266.herokuapp.com"
android:pathPrefix="/onsuccess" />
</intent-filter>
</activity>

<activity
android:name=".MainActivity"
android:exported="true"
android:label="@string/app_name"
android:theme="@style/Theme.AndroidPayPalWebSdkIntegrationExample">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>

</manifest>
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package com.example.androidpaypalwebsdkintegrationexample;

import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;

import com.example.androidpaypalwebsdkintegrationexample.databinding.FragmentCatalogBinding;

/**
* This Fragment is a sample fragment to demonstrate a view to host the fully Catalog of products
* the merchant's android app may be offering.
*/
public class CatalogFragment extends Fragment {

private FragmentCatalogBinding binding;

@Override
public View onCreateView(
LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState
) {
binding = FragmentCatalogBinding.inflate(inflater, container, false);
return binding.getRoot();
}

@Override
public void onDestroyView() {
super.onDestroyView();
binding = null;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package com.example.androidpaypalwebsdkintegrationexample;

import android.os.Bundle;

import androidx.appcompat.app.AppCompatActivity;
import androidx.navigation.NavController;
import androidx.navigation.Navigation;
import androidx.navigation.ui.AppBarConfiguration;
import androidx.navigation.ui.NavigationUI;

import com.example.androidpaypalwebsdkintegrationexample.databinding.ActivityMainBinding;

public class MainActivity extends AppCompatActivity {

private AppBarConfiguration appBarConfiguration;
private ActivityMainBinding binding;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

binding = ActivityMainBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());

setSupportActionBar(binding.toolbar);

NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment_content_main);
appBarConfiguration = new AppBarConfiguration.Builder(navController.getGraph()).build();
NavigationUI.setupActionBarWithNavController(this, navController, appBarConfiguration);
}

@Override
public boolean onSupportNavigateUp() {
NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment_content_main);
return NavigationUI.navigateUp(navController, appBarConfiguration)
|| super.onSupportNavigateUp();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package com.example.androidpaypalwebsdkintegrationexample;

import androidx.appcompat.app.AppCompatActivity;

import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
import android.widget.TextView;

import com.example.androidpaypalwebsdkintegrationexample.databinding.ActivityOnPaymentCompletionBinding;

import org.json.JSONObject;

/**
* This is a sample activity which demonstrates an example payment success activity page.
* This Activity should be invoked after the buyer has reviewed and approved their payment
* on web application hosted by merchant.
* The web application should use payment methods like PayPal, Braintree to collect payments from buyer
*/
public class OnPaymentCompletionActivity extends AppCompatActivity {
private ActivityOnPaymentCompletionBinding binding;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_on_payment_completion);
binding = ActivityOnPaymentCompletionBinding.inflate(getLayoutInflater());
// get can also get the appLinkIntent using getIntent() to retrieve any associated data
}

@Override
protected void onStart() {
super.onStart();
Uri uri = getIntent().getData();
String payPalOrderID = null;

if (uri != null) {
payPalOrderID = uri.getQueryParameter("payPalOrderID");
Log.i("OnPayment", "onStart payPalOrderID = " + payPalOrderID);
} else {
Log.i("OnPayment", "onStart No payPalOrderID Found");
}

TextView textView = (TextView) findViewById(R.id.payPalOrderId);
textView.setText(payPalOrderID);

// Fetch the PayPal Order Details from your REST APIs. The REST APIs should only exposes minimal data as required by your APP.
// You should also capture or authorize the amounts before fulfilling the buyer order.
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
package com.example.androidpaypalwebsdkintegrationexample;

import android.net.Uri;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import androidx.annotation.NonNull;
import androidx.browser.customtabs.CustomTabsIntent;
import androidx.fragment.app.Fragment;
import androidx.navigation.fragment.NavHostFragment;

import com.example.androidpaypalwebsdkintegrationexample.databinding.FragmentShoppingCartBinding;

/**
* This Fragment demonstrates a sample shopping cart fragment which should be rendered after buyer
* has added items to their cart and clicks shopping cart button.
* This view should also render a list of selected payment methods like PayPal and link / button to
* view full list of all available payment options.
* <p>
* This example demonstrates two buttons,
* 1. Render a PayPal Web SDK integration and Orders API using a Chrome Custom Tab
* 2. Render a PayPal Checkout integration using Orders API.
* <p>
* The buttons rendered on this page takes the buyers to a web application hosted by merchant which should
* lists down payment methods available, e.g. PayPal.
* <p>
* When the buyer clicks the PayPal button inside the Chrome Custom tab, they would be redirected
* to PayPal.com to review and approve their payment.
* After they approve the payment on PayPal.com, they would be taken back to the web application
* hosted by the merchant.
* <p>
* The web application needs to render a redirect button which should take the buyers back to this
* Android App using App Links/Deep Links.
*/
public class ShoppingCartFragment extends Fragment {

private final String merchantHostedCheckoutWebUrlSpb = "https://shipping-change-sandbox-e66e294a9266.herokuapp.com/buttons.html?onApproveUrl=https://shipping-change-sandbox-e66e294a9266.herokuapp.com/onsuccess/&onCancelUrl=https://shipping-change-sandbox-e66e294a9266.herokuapp.com/redirect-to-app/oncancel/";
private final String merchantHostedCheckoutWebUrlApi = "https://shipping-change-sandbox-e66e294a9266.herokuapp.com/api-integration.html?onApproveUrl=https://shipping-change-sandbox-e66e294a9266.herokuapp.com/onsuccess/&onCancelUrl=https://shipping-change-sandbox-e66e294a9266.herokuapp.com/redirect-to-app/oncancel/";

private FragmentShoppingCartBinding binding;

@Override
public View onCreateView(
LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState
) {

binding = FragmentShoppingCartBinding.inflate(inflater, container, false);
return binding.getRoot();

}

public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);

Uri payPalSpbUrl = Uri.parse(merchantHostedCheckoutWebUrlSpb);
Uri payPalApiCheckoutUrl = Uri.parse(merchantHostedCheckoutWebUrlApi);
CustomTabsIntent customTabsIntent = new CustomTabsIntent.Builder().build();

binding.checkoutButtonPaypalSpb.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
customTabsIntent.launchUrl(binding.getRoot().getContext(), payPalSpbUrl);
}
});
binding.checkoutButtonPaypalApi.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
customTabsIntent.launchUrl(binding.getRoot().getContext(), payPalApiCheckoutUrl);
}
});
binding.productCatalogButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
NavHostFragment.findNavController(ShoppingCartFragment.this)
.navigate(R.id.action_ShoppingCartFragment_to_CatalogFragment);
}
});
}

@Override
public void onDestroyView() {
super.onDestroyView();
binding = null;
}
}
Loading

0 comments on commit dd75e30

Please sign in to comment.