From 8427bcdbd6b8391240bae50e2dcc1d53140a545a Mon Sep 17 00:00:00 2001 From: warmkesselj <150195168+warmkesselj@users.noreply.github.com> Date: Mon, 9 Dec 2024 12:01:52 -0800 Subject: [PATCH] Shopper insights rp1 feature include session (#1235) * Add shopper insights session ID to paypalRequest * Update factory for paypal request * remove extra setting of email address * Remove optional null and check for null in PayPalRequestFactory * Use String type and fix check for null to prevent run time crashes * Remove DEMO UI updates * Update comment for shopperSessionId * Add experimentalAPI for shopper session Id * Remove unused shopperInsightsSessionIdNullSwitch * Check for null and empty string * Update to use putOpt method for shopperId * Update changelog * Check for null and empty strings for request properties. * Update unit tests * Update test * Update unit tests * Update changelog --- CHANGELOG.md | 2 ++ .../demo/PayPalFragment.java | 6 ++-- .../demo/PayPalRequestFactory.java | 35 ++++++++++++++----- .../demo/ShopperInsightsFragment.kt | 8 +++-- .../api/paypal/PayPalCheckoutRequest.kt | 4 +++ .../api/paypal/PayPalRequest.kt | 7 ++++ .../api/paypal/PayPalVaultRequest.kt | 4 +++ .../paypal/PayPalCheckoutRequestUnitTest.java | 19 ++++++++++ .../paypal/PayPalVaultRequestUnitTest.java | 21 ++++++++++- 9 files changed, 93 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b8cea1961c..4ef49bf35d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ * ShopperInsights (BETA) * Add `isPayPalAppInstalled` and `isVenmoAppInstalled` methods * Add `shopperSessionId` parameter to `ShopperInsightsClient` +* BraintreePayPal + * Add `shopperSessionId` to `PayPalCheckoutRequest` and `PayPalVaultRequest` ## 5.2.0 (2024-10-30) diff --git a/Demo/src/main/java/com/braintreepayments/demo/PayPalFragment.java b/Demo/src/main/java/com/braintreepayments/demo/PayPalFragment.java index fa90df0b62..5c499719e5 100644 --- a/Demo/src/main/java/com/braintreepayments/demo/PayPalFragment.java +++ b/Demo/src/main/java/com/braintreepayments/demo/PayPalFragment.java @@ -148,7 +148,8 @@ private void launchPayPal( activity, buyerEmailAddress, buyerPhoneCountryCode, - buyerPhoneNationalNumber + buyerPhoneNationalNumber, + null ); } else { payPalRequest = createPayPalCheckoutRequest( @@ -156,7 +157,8 @@ private void launchPayPal( amount, buyerEmailAddress, buyerPhoneCountryCode, - buyerPhoneNationalNumber + buyerPhoneNationalNumber, + null ); } payPalClient.createPaymentAuthRequest(requireContext(), payPalRequest, diff --git a/Demo/src/main/java/com/braintreepayments/demo/PayPalRequestFactory.java b/Demo/src/main/java/com/braintreepayments/demo/PayPalRequestFactory.java index efdc0d8bd5..2331067882 100644 --- a/Demo/src/main/java/com/braintreepayments/demo/PayPalRequestFactory.java +++ b/Demo/src/main/java/com/braintreepayments/demo/PayPalRequestFactory.java @@ -25,17 +25,27 @@ public static PayPalVaultRequest createPayPalVaultRequest( Context context, String buyerEmailAddress, String buyerPhoneCountryCode, - String buyerPhoneNationalNumber + String buyerPhoneNationalNumber, + String shopperInsightsSessionId ) { PayPalVaultRequest request = new PayPalVaultRequest(true); - if (!buyerEmailAddress.isEmpty()) { + if (buyerEmailAddress != null && !buyerEmailAddress.isEmpty()) { request.setUserAuthenticationEmail(buyerEmailAddress); } - if (!buyerPhoneCountryCode.isEmpty() && !buyerPhoneNationalNumber.isEmpty()) { - request.setUserPhoneNumber(new PayPalPhoneNumber(buyerPhoneCountryCode, buyerPhoneNationalNumber)); + if ((buyerPhoneCountryCode != null && !buyerPhoneCountryCode.isEmpty()) + && (buyerPhoneNationalNumber != null && !buyerPhoneNationalNumber.isEmpty())) { + + request.setUserPhoneNumber(new PayPalPhoneNumber( + buyerPhoneCountryCode, + buyerPhoneNationalNumber) + ); + } + + if (shopperInsightsSessionId != null && !shopperInsightsSessionId.isEmpty()) { + request.setShopperSessionId(shopperInsightsSessionId); } if (Settings.isPayPalAppSwithEnabled(context)) { @@ -111,16 +121,25 @@ public static PayPalCheckoutRequest createPayPalCheckoutRequest( String amount, String buyerEmailAddress, String buyerPhoneCountryCode, - String buyerPhoneNationalNumber + String buyerPhoneNationalNumber, + String shopperInsightsSessionId ) { PayPalCheckoutRequest request = new PayPalCheckoutRequest(amount, true); - if (!buyerEmailAddress.isEmpty()) { + if (buyerEmailAddress != null && !buyerEmailAddress.isEmpty()) { request.setUserAuthenticationEmail(buyerEmailAddress); } - if (!buyerPhoneCountryCode.isEmpty() && !buyerPhoneNationalNumber.isEmpty()) { - request.setUserPhoneNumber(new PayPalPhoneNumber(buyerPhoneCountryCode, buyerPhoneNationalNumber)); + if ((buyerPhoneCountryCode != null && !buyerPhoneCountryCode.isEmpty()) + && (buyerPhoneNationalNumber != null && !buyerPhoneNationalNumber.isEmpty())) { + request.setUserPhoneNumber(new PayPalPhoneNumber( + buyerPhoneCountryCode, + buyerPhoneNationalNumber) + ); + } + + if (shopperInsightsSessionId != null && !shopperInsightsSessionId.isEmpty()) { + request.setShopperSessionId(shopperInsightsSessionId); } request.setDisplayName(Settings.getPayPalDisplayName(context)); diff --git a/Demo/src/main/java/com/braintreepayments/demo/ShopperInsightsFragment.kt b/Demo/src/main/java/com/braintreepayments/demo/ShopperInsightsFragment.kt index 685b7f4328..aa4f4065ee 100644 --- a/Demo/src/main/java/com/braintreepayments/demo/ShopperInsightsFragment.kt +++ b/Demo/src/main/java/com/braintreepayments/demo/ShopperInsightsFragment.kt @@ -60,12 +60,14 @@ class ShopperInsightsFragment : BaseFragment() { private lateinit var venmoStartedPendingRequest: VenmoPendingRequest.Started private lateinit var paypalStartedPendingRequest: PayPalPendingRequest.Started + private var shopperSessionId: String = "test-shopper-session-id" + override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { - shopperInsightsClient = ShopperInsightsClient(requireContext(), authStringArg, "test-shopper-session-id") + shopperInsightsClient = ShopperInsightsClient(requireContext(), authStringArg, shopperSessionId) venmoClient = VenmoClient(requireContext(), super.getAuthStringArg(), null) payPalClient = PayPalClient( @@ -229,7 +231,9 @@ class ShopperInsightsFragment : BaseFragment() { activity, emailInput.editText?.text.toString(), countryCodeInput.editText?.text.toString(), - nationalNumberInput.editText?.text.toString() + nationalNumberInput.editText?.text.toString(), + shopperSessionId + ) ) { authRequest -> when (authRequest) { diff --git a/PayPal/src/main/java/com/braintreepayments/api/paypal/PayPalCheckoutRequest.kt b/PayPal/src/main/java/com/braintreepayments/api/paypal/PayPalCheckoutRequest.kt index e95d708813..66b51a9baa 100644 --- a/PayPal/src/main/java/com/braintreepayments/api/paypal/PayPalCheckoutRequest.kt +++ b/PayPal/src/main/java/com/braintreepayments/api/paypal/PayPalCheckoutRequest.kt @@ -4,6 +4,7 @@ import android.text.TextUtils import com.braintreepayments.api.core.Authorization import com.braintreepayments.api.core.ClientToken import com.braintreepayments.api.core.Configuration +import com.braintreepayments.api.core.ExperimentalBetaApi import com.braintreepayments.api.core.PostalAddress import com.braintreepayments.api.core.PostalAddressParser import kotlinx.parcelize.Parcelize @@ -91,6 +92,7 @@ class PayPalCheckoutRequest @JvmOverloads constructor( lineItems = lineItems ) { + @OptIn(ExperimentalBetaApi::class) @Throws(JSONException::class) @Suppress("LongMethod", "CyclomaticComplexMethod") override fun createRequestBody( @@ -126,6 +128,8 @@ class PayPalCheckoutRequest @JvmOverloads constructor( userPhoneNumber?.let { parameters.put(PHONE_NUMBER_KEY, it.toJson()) } + parameters.putOpt(SHOPPER_SESSION_ID, shopperSessionId) + if (currencyCode == null) { currencyCode = configuration?.payPalCurrencyIsoCode } diff --git a/PayPal/src/main/java/com/braintreepayments/api/paypal/PayPalRequest.kt b/PayPal/src/main/java/com/braintreepayments/api/paypal/PayPalRequest.kt index adfd68924c..d5b687e97f 100644 --- a/PayPal/src/main/java/com/braintreepayments/api/paypal/PayPalRequest.kt +++ b/PayPal/src/main/java/com/braintreepayments/api/paypal/PayPalRequest.kt @@ -4,6 +4,7 @@ import android.os.Parcelable import androidx.annotation.RestrictTo import com.braintreepayments.api.core.Authorization import com.braintreepayments.api.core.Configuration +import com.braintreepayments.api.core.ExperimentalBetaApi import com.braintreepayments.api.core.PostalAddress import org.json.JSONException @@ -72,6 +73,8 @@ import org.json.JSONException * where the user has a PayPal Account with the same email. * @property userPhoneNumber User phone number used to initiate a quicker authentication flow in * cases where the user has a PayPal Account with the phone number. + * @property shopperSessionId the shopper session ID returned from your shopper insights server SDK + * integration * @property lineItems The line items for this transaction. It can include up to 249 line items. */ abstract class PayPalRequest internal constructor( @@ -87,6 +90,9 @@ abstract class PayPalRequest internal constructor( open var riskCorrelationId: String? = null, open var userAuthenticationEmail: String? = null, open var userPhoneNumber: PayPalPhoneNumber? = null, + + @property:ExperimentalBetaApi + open var shopperSessionId: String? = null, open var lineItems: List = emptyList() ) : Parcelable { @@ -132,5 +138,6 @@ abstract class PayPalRequest internal constructor( internal const val PLAN_TYPE_KEY: String = "plan_type" internal const val PLAN_METADATA_KEY: String = "plan_metadata" internal const val PHONE_NUMBER_KEY: String = "phone_number" + internal const val SHOPPER_SESSION_ID: String = "shopper_session_id" } } diff --git a/PayPal/src/main/java/com/braintreepayments/api/paypal/PayPalVaultRequest.kt b/PayPal/src/main/java/com/braintreepayments/api/paypal/PayPalVaultRequest.kt index ea19bfb618..5a21564f62 100644 --- a/PayPal/src/main/java/com/braintreepayments/api/paypal/PayPalVaultRequest.kt +++ b/PayPal/src/main/java/com/braintreepayments/api/paypal/PayPalVaultRequest.kt @@ -5,6 +5,7 @@ import android.text.TextUtils import com.braintreepayments.api.core.Authorization import com.braintreepayments.api.core.ClientToken import com.braintreepayments.api.core.Configuration +import com.braintreepayments.api.core.ExperimentalBetaApi import com.braintreepayments.api.core.PostalAddress import com.braintreepayments.api.core.PostalAddressParser import kotlinx.parcelize.Parcelize @@ -65,6 +66,7 @@ class PayPalVaultRequest lineItems = lineItems ) { + @OptIn(ExperimentalBetaApi::class) @Throws(JSONException::class) @Suppress("LongMethod") override fun createRequestBody( @@ -92,6 +94,8 @@ class PayPalVaultRequest parameters.putOpt(PAYER_EMAIL_KEY, userAuthenticationEmail) + parameters.putOpt(SHOPPER_SESSION_ID, shopperSessionId) + userPhoneNumber?.let { parameters.put(PHONE_NUMBER_KEY, it.toJson()) } if (enablePayPalAppSwitch && !appLink.isNullOrEmpty() && !userAuthenticationEmail.isNullOrEmpty()) { diff --git a/PayPal/src/test/java/com/braintreepayments/api/paypal/PayPalCheckoutRequestUnitTest.java b/PayPal/src/test/java/com/braintreepayments/api/paypal/PayPalCheckoutRequestUnitTest.java index 8752cde5f0..d0a8f9118f 100644 --- a/PayPal/src/test/java/com/braintreepayments/api/paypal/PayPalCheckoutRequestUnitTest.java +++ b/PayPal/src/test/java/com/braintreepayments/api/paypal/PayPalCheckoutRequestUnitTest.java @@ -1,5 +1,6 @@ package com.braintreepayments.api.paypal; +import android.os.Build; import android.os.Parcel; import com.braintreepayments.api.core.Authorization; @@ -27,6 +28,7 @@ public class PayPalCheckoutRequestUnitTest { public void newPayPalCheckoutRequest_setsDefaultValues() { PayPalCheckoutRequest request = new PayPalCheckoutRequest("1.00", false); + assertNull(request.getShopperSessionId()); assertNotNull(request.getAmount()); assertNull(request.getCurrencyCode()); assertNull(request.getLocaleCode()); @@ -44,6 +46,7 @@ public void newPayPalCheckoutRequest_setsDefaultValues() { public void setsValuesCorrectly() { PostalAddress postalAddress = new PostalAddress(); PayPalCheckoutRequest request = new PayPalCheckoutRequest("1.00", true); + request.setShopperSessionId("shopper-insights-id"); request.setCurrencyCode("USD"); request.setShouldOfferPayLater(true); request.setIntent(PayPalPaymentIntent.SALE); @@ -58,6 +61,7 @@ public void setsValuesCorrectly() { request.setRiskCorrelationId("123-correlation"); request.setLandingPageType(PayPalLandingPageType.LANDING_PAGE_TYPE_LOGIN); + assertEquals("shopper-insights-id", request.getShopperSessionId()); assertEquals("1.00", request.getAmount()); assertEquals("USD", request.getCurrencyCode()); assertEquals("US", request.getLocaleCode()); @@ -156,6 +160,21 @@ public void createRequestBody_does_not_set_userAuthenticationEmail_when_email_is assertFalse(requestBody.contains("\"payer_email\":" + "\"" + payerEmail + "\"")); } + @Test + public void createRequestBody_sets_shopper_insights_session_id() throws JSONException { + PayPalCheckoutRequest request = new PayPalCheckoutRequest("1.00", true); + request.setShopperSessionId("shopper-insights-id"); + String requestBody = request.createRequestBody( + mock(Configuration.class), + mock(Authorization.class), + "success_url", + "cancel_url", + "universal_url" + ); + + assertTrue(requestBody.contains("\"shopper_session_id\":" + "\"shopper-insights-id\"")); + } + @Test public void createRequestBody_sets_userPhoneNumber_when_not_null() throws JSONException { PayPalCheckoutRequest request = new PayPalCheckoutRequest("1.00", true); diff --git a/PayPal/src/test/java/com/braintreepayments/api/paypal/PayPalVaultRequestUnitTest.java b/PayPal/src/test/java/com/braintreepayments/api/paypal/PayPalVaultRequestUnitTest.java index 350e1c9ad5..4cac7b935a 100644 --- a/PayPal/src/test/java/com/braintreepayments/api/paypal/PayPalVaultRequestUnitTest.java +++ b/PayPal/src/test/java/com/braintreepayments/api/paypal/PayPalVaultRequestUnitTest.java @@ -16,6 +16,7 @@ import com.braintreepayments.api.testutils.Fixtures; import org.json.JSONException; +import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.RobolectricTestRunner; @@ -32,6 +33,7 @@ public class PayPalVaultRequestUnitTest { public void newPayPalVaultRequest_setsDefaultValues() { PayPalVaultRequest request = new PayPalVaultRequest(false); + assertNull(request.getShopperSessionId()); assertNull(request.getLocaleCode()); assertFalse(request.isShippingAddressRequired()); assertNull(request.getShippingAddressOverride()); @@ -46,6 +48,7 @@ public void newPayPalVaultRequest_setsDefaultValues() { public void setsValuesCorrectly() { PostalAddress postalAddress = new PostalAddress(); PayPalVaultRequest request = new PayPalVaultRequest(true); + request.setShopperSessionId("shopper-insights-id"); request.setLocaleCode("US"); request.setBillingAgreementDescription("Billing Agreement Description"); request.setShippingAddressRequired(true); @@ -75,6 +78,7 @@ public void setsValuesCorrectly() { request.setRecurringBillingDetails(billingDetails); request.setRecurringBillingPlanType(PayPalRecurringBillingPlanType.RECURRING); + assertEquals("shopper-insights-id", request.getShopperSessionId()); assertEquals("US", request.getLocaleCode()); assertEquals("Billing Agreement Description", request.getBillingAgreementDescription()); assertTrue(request.isShippingAddressRequired()); @@ -212,11 +216,11 @@ public void createRequestBody_sets_userAuthenticationEmail_when_not_null() throw assertTrue(requestBody.contains("\"payer_email\":" + "\"" + payerEmail + "\"")); } + @Test public void createRequestBody_sets_enablePayPalSwitch_and_userAuthenticationEmail_not_null() throws JSONException { String versionSDK = String.valueOf(Build.VERSION.SDK_INT); String payerEmail = "payer_email@example.com"; PayPalVaultRequest request = new PayPalVaultRequest(true); - request.setEnablePayPalAppSwitch(true); request.setUserAuthenticationEmail(payerEmail); String requestBody = request.createRequestBody( @@ -233,6 +237,21 @@ public void createRequestBody_sets_enablePayPalSwitch_and_userAuthenticationEmai assertTrue(requestBody.contains("\"merchant_app_return_url\":" + "\"universal_url\"")); } + @Test + public void createRequestBody_sets_shopper_insights_session_id() throws JSONException { + PayPalVaultRequest request = new PayPalVaultRequest(true); + request.setShopperSessionId("shopper-insights-id"); + String requestBody = request.createRequestBody( + mock(Configuration.class), + mock(Authorization.class), + "success_url", + "cancel_url", + "universal_url" + ); + + assertTrue(requestBody.contains("\"shopper_session_id\":" + "\"shopper-insights-id\"")); + } + @Test public void createRequestBody_correctlyFormatsJSON() throws JSONException { PayPalVaultRequest request = new PayPalVaultRequest(true);