├── .github
├── CODEOWNERS
├── ISSUE_TEMPLATE
│ ├── bug_report.yml
│ ├── config.yml
│ └── feature_request.yml
├── PULL_REQUEST_TEMPLATE.md
├── actions
│ ├── decode_signing_key_action
│ │ └── action.yml
│ ├── publish_all_modules
│ │ └── action.yml
│ ├── set_github_user
│ │ └── action.yml
│ └── unit_test_module
│ │ └── action.yml
└── workflows
│ ├── build.yml
│ ├── release.yml
│ ├── release_demo.yml
│ ├── release_snapshot.yml
│ ├── static_analysis.yml
│ └── tests.yml
├── .gitignore
├── .idea
├── .gitignore
├── .name
├── compiler.xml
└── misc.xml
├── CHANGELOG.md
├── CardPayments
├── .gitignore
├── build.gradle
├── consumer-rules.pro
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── paypal
│ │ └── android
│ │ └── cardpayments
│ │ └── ExampleInstrumentedTest.kt
│ ├── main
│ ├── AndroidManifest.xml
│ ├── java
│ │ └── com
│ │ │ └── paypal
│ │ │ └── android
│ │ │ └── cardpayments
│ │ │ ├── ApproveOrderMetadata.kt
│ │ │ ├── Card.kt
│ │ │ ├── CardApproveOrderCallback.kt
│ │ │ ├── CardApproveOrderResult.kt
│ │ │ ├── CardAuthChallenge.kt
│ │ │ ├── CardAuthLauncher.kt
│ │ │ ├── CardClient.kt
│ │ │ ├── CardError.kt
│ │ │ ├── CardErrorCode.kt
│ │ │ ├── CardFinishApproveOrderResult.kt
│ │ │ ├── CardFinishVaultResult.kt
│ │ │ ├── CardPresentAuthChallengeResult.kt
│ │ │ ├── CardRequest.kt
│ │ │ ├── CardRequestFactory.kt
│ │ │ ├── CardResponseParser.kt
│ │ │ ├── CardVaultCallback.kt
│ │ │ ├── CardVaultRequest.kt
│ │ │ ├── CardVaultResult.kt
│ │ │ ├── DataVaultPaymentMethodTokensAPI.kt
│ │ │ ├── UpdateSetupTokenResult.kt
│ │ │ ├── analytics
│ │ │ ├── ApproveOrderEvent.kt
│ │ │ ├── CardAnalytics.kt
│ │ │ └── VaultEvent.kt
│ │ │ ├── api
│ │ │ ├── CheckoutOrdersAPI.kt
│ │ │ └── ConfirmPaymentSourceResult.kt
│ │ │ ├── model
│ │ │ ├── Amount.kt
│ │ │ ├── AuthenticationResult.kt
│ │ │ ├── Payee.kt
│ │ │ ├── PaymentSource.kt
│ │ │ └── PurchaseUnit.kt
│ │ │ └── threedsecure
│ │ │ ├── SCA.kt
│ │ │ └── ThreeDSecureResult.kt
│ └── res
│ │ └── raw
│ │ └── graphql_query_update_setup_token.graphql
│ └── test
│ ├── java
│ └── com
│ │ └── paypal
│ │ └── android
│ │ └── cardpayments
│ │ ├── ApproveOrderMetadataUnitTest.kt
│ │ ├── CardAuthLauncherUnitTest.kt
│ │ ├── CardClientUnitTest.kt
│ │ ├── CardRequestFactoryUnitTest.kt
│ │ ├── CardRequestParserUnitTest.kt
│ │ ├── CheckoutOrdersAPIUnitTest.kt
│ │ └── DataVaultPaymentMethodTokensAPIUnitTest.kt
│ └── resources
│ └── robolectric.properties
├── CorePayments
├── .gitignore
├── build.gradle
├── consumer-rules.pro
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── paypal
│ │ └── android
│ │ └── corepayments
│ │ └── HttpIntegrationTest.kt
│ ├── main
│ ├── AndroidManifest.xml
│ └── java
│ │ └── com
│ │ └── paypal
│ │ └── android
│ │ └── corepayments
│ │ ├── APIClientError.kt
│ │ ├── APIRequest.kt
│ │ ├── Address.kt
│ │ ├── Base64.kt
│ │ ├── BrowserSwitchRequestCodes.kt
│ │ ├── CoreConfig.kt
│ │ ├── CoreCoroutineExceptionHandler.kt
│ │ ├── Environment.kt
│ │ ├── Http.kt
│ │ ├── HttpMethod.kt
│ │ ├── HttpRequest.kt
│ │ ├── HttpResponse.kt
│ │ ├── HttpResponseParser.kt
│ │ ├── LoadRawResourceResult.kt
│ │ ├── OrderErrorDetail.kt
│ │ ├── OrderStatus.kt
│ │ ├── PayPalSDKError.kt
│ │ ├── PayPalSDKErrorCode.kt
│ │ ├── PaymentsJSON.kt
│ │ ├── ResourceLoader.kt
│ │ ├── RestClient.kt
│ │ ├── TrackingEventsAPI.kt
│ │ ├── analytics
│ │ ├── AnalyticsEventData.kt
│ │ ├── AnalyticsService.kt
│ │ ├── DeviceData.kt
│ │ └── DeviceInspector.kt
│ │ └── graphql
│ │ ├── GraphQLClient.kt
│ │ ├── GraphQLError.kt
│ │ ├── GraphQLExtension.kt
│ │ └── GraphQLResult.kt
│ └── test
│ ├── java
│ └── com
│ │ └── paypal
│ │ └── android
│ │ └── corepayments
│ │ ├── CoreConfigUnitTest.kt
│ │ ├── EnvironmentUnitTest.kt
│ │ ├── HttpResponseParserUnitTest.kt
│ │ ├── HttpUnitTest.kt
│ │ ├── PaymentsJSONUnitTest.kt
│ │ ├── RestClientUnitTest.kt
│ │ ├── TestUtils.kt
│ │ ├── TrackingEventsAPIUnitTest.kt
│ │ ├── analytics
│ │ ├── AnalyticsServiceTest.kt
│ │ └── DeviceInspectorUnitTest.kt
│ │ └── graphql
│ │ └── GraphQLClientUnitTest.kt
│ └── resources
│ └── robolectric.properties
├── Demo
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── paypal
│ │ └── android
│ │ └── CardTest.kt
│ ├── androidTestShared
│ └── java
│ │ └── com
│ │ └── paypal
│ │ └── android
│ │ └── testutils
│ │ └── AppDriver.kt
│ ├── main
│ ├── AndroidManifest.xml
│ ├── ic_launcher-playstore.png
│ ├── java
│ │ └── com
│ │ │ └── paypal
│ │ │ └── android
│ │ │ ├── MainActivity.kt
│ │ │ ├── MainApplication.kt
│ │ │ ├── api
│ │ │ ├── model
│ │ │ │ ├── CardPaymentToken.kt
│ │ │ │ ├── CardSetupToken.kt
│ │ │ │ ├── ClientId.kt
│ │ │ │ ├── Order.kt
│ │ │ │ ├── OrderIntent.kt
│ │ │ │ ├── PayPalPaymentToken.kt
│ │ │ │ └── PayPalSetupToken.kt
│ │ │ └── services
│ │ │ │ ├── MerchantIntegration.kt
│ │ │ │ ├── SDKSampleServerAPI.kt
│ │ │ │ ├── SDKSampleServerException.kt
│ │ │ │ └── SDKSampleServerResult.kt
│ │ │ ├── di
│ │ │ └── NetworkModule.kt
│ │ │ ├── models
│ │ │ ├── OrderRequest.kt
│ │ │ └── TestCard.kt
│ │ │ ├── ui
│ │ │ ├── DemoApp.kt
│ │ │ ├── DemoAppDestinations.kt
│ │ │ ├── approveorder
│ │ │ │ ├── ApproveOrderForm.kt
│ │ │ │ ├── ApproveOrderUiState.kt
│ │ │ │ ├── ApproveOrderView.kt
│ │ │ │ ├── ApproveOrderViewModel.kt
│ │ │ │ ├── CardFormatter.kt
│ │ │ │ ├── CardNumberVisualTransformation.kt
│ │ │ │ ├── CardType.kt
│ │ │ │ ├── DateString.kt
│ │ │ │ ├── DateVisualTransformation.kt
│ │ │ │ ├── OrderInfo.kt
│ │ │ │ └── SetupTokenInfo.kt
│ │ │ ├── features
│ │ │ │ ├── Feature.kt
│ │ │ │ └── FeaturesView.kt
│ │ │ ├── paypalbuttons
│ │ │ │ ├── ButtonFundingType.kt
│ │ │ │ ├── PayPalButtonColorOptionList.kt
│ │ │ │ ├── PayPalButtonFundingTypeOptionList.kt
│ │ │ │ ├── PayPalButtonLabelOptionList.kt
│ │ │ │ ├── PayPalButtonsUiState.kt
│ │ │ │ ├── PayPalButtonsView.kt
│ │ │ │ ├── PayPalButtonsViewModel.kt
│ │ │ │ ├── PayPalCreditButtonColorOptionList.kt
│ │ │ │ ├── PaymentButtonShapeOptionList.kt
│ │ │ │ └── PaymentButtonSizeOptionList.kt
│ │ │ ├── paypalstaticbuttons
│ │ │ │ └── PayPalStaticButtonsView.kt
│ │ │ ├── paypalweb
│ │ │ │ ├── PayPalWebCheckoutResultView.kt
│ │ │ │ ├── PayPalWebUiState.kt
│ │ │ │ ├── PayPalWebView.kt
│ │ │ │ ├── PayPalWebViewModel.kt
│ │ │ │ └── StartPayPalWebCheckoutForm.kt
│ │ │ ├── paypalwebvault
│ │ │ │ ├── PayPalWebVaultUiState.kt
│ │ │ │ ├── PayPalWebVaultView.kt
│ │ │ │ └── PayPalWebVaultViewModel.kt
│ │ │ ├── selectcard
│ │ │ │ ├── SelectCardView.kt
│ │ │ │ └── SelectCardViewModel.kt
│ │ │ └── vaultcard
│ │ │ │ ├── VaultCardUiState.kt
│ │ │ │ ├── VaultCardView.kt
│ │ │ │ └── VaultCardViewModel.kt
│ │ │ ├── uishared
│ │ │ ├── components
│ │ │ │ ├── ActionButton.kt
│ │ │ │ ├── ActionButtonColumn.kt
│ │ │ │ ├── BooleanOptionList.kt
│ │ │ │ ├── CardForm.kt
│ │ │ │ ├── CardPaymentTokenView.kt
│ │ │ │ ├── CardResultView.kt
│ │ │ │ ├── CardSetupTokenView.kt
│ │ │ │ ├── CardVaultResultView.kt
│ │ │ │ ├── CreateOrderForm.kt
│ │ │ │ ├── CreateOrderWithVaultOptionForm.kt
│ │ │ │ ├── DemoAppTopBar.kt
│ │ │ │ ├── EnumOptionList.kt
│ │ │ │ ├── ErrorView.kt
│ │ │ │ ├── InfoColumn.kt
│ │ │ │ ├── IntSlider.kt
│ │ │ │ ├── OptionList.kt
│ │ │ │ ├── OrderView.kt
│ │ │ │ ├── PayPalPaymentTokenView.kt
│ │ │ │ ├── PayPalSetupTokenView.kt
│ │ │ │ ├── PropertyView.kt
│ │ │ │ ├── StepHeader.kt
│ │ │ │ └── UriView.kt
│ │ │ ├── effects
│ │ │ │ └── NavDestinationChangeDisposableEffect.kt
│ │ │ ├── enums
│ │ │ │ └── StoreInVaultOption.kt
│ │ │ └── state
│ │ │ │ ├── ActionState.kt
│ │ │ │ └── CompletedActionState.kt
│ │ │ ├── usecase
│ │ │ ├── CompleteOrderUseCase.kt
│ │ │ ├── CreateCardPaymentTokenUseCase.kt
│ │ │ ├── CreateCardSetupTokenUseCase.kt
│ │ │ ├── CreateOrderUseCase.kt
│ │ │ ├── CreatePayPalPaymentTokenUseCase.kt
│ │ │ ├── CreatePayPalSetupTokenUseCase.kt
│ │ │ ├── GetClientIdUseCase.kt
│ │ │ └── GetSetupTokenUseCase.kt
│ │ │ └── utils
│ │ │ ├── ContextExt.kt
│ │ │ ├── OnLifecycleOwnerResumeEffect.kt
│ │ │ ├── OnNewIntentEffect.kt
│ │ │ └── UIConstants.kt
│ ├── play
│ │ ├── default-language.txt
│ │ ├── listings
│ │ │ └── en-US
│ │ │ │ └── title.txt
│ │ └── release-notes
│ │ │ └── en-US
│ │ │ └── internal.txt
│ └── res
│ │ ├── drawable
│ │ └── chevron.xml
│ │ ├── layout
│ │ └── pay_later_button_test_layout.xml
│ │ ├── mipmap-anydpi-v26
│ │ ├── ic_launcher.xml
│ │ └── ic_launcher_round.xml
│ │ ├── mipmap-hdpi
│ │ ├── ic_launcher.webp
│ │ ├── ic_launcher_foreground.webp
│ │ └── ic_launcher_round.webp
│ │ ├── mipmap-mdpi
│ │ ├── ic_launcher.webp
│ │ ├── ic_launcher_foreground.webp
│ │ └── ic_launcher_round.webp
│ │ ├── mipmap-xhdpi
│ │ ├── ic_launcher.webp
│ │ ├── ic_launcher_foreground.webp
│ │ └── ic_launcher_round.webp
│ │ ├── mipmap-xxhdpi
│ │ ├── ic_launcher.webp
│ │ ├── ic_launcher_foreground.webp
│ │ └── ic_launcher_round.webp
│ │ ├── mipmap-xxxhdpi
│ │ ├── ic_launcher.webp
│ │ ├── ic_launcher_foreground.webp
│ │ └── ic_launcher_round.webp
│ │ ├── values-night
│ │ └── themes.xml
│ │ ├── values
│ │ ├── colors.xml
│ │ ├── strings.xml
│ │ └── themes.xml
│ │ └── xml
│ │ └── network_security_config.xml
│ └── test
│ └── java
│ └── com
│ └── paypal
│ └── android
│ └── ExampleUnitTest.kt
├── FraudProtection
├── .gitignore
├── build.gradle
├── consumer-rules.pro
├── libs
│ └── android-magnessdk-5.5.1.jar
├── proguard-rules.pro
└── src
│ ├── main
│ ├── AndroidManifest.xml
│ └── java
│ │ └── com
│ │ └── paypal
│ │ └── android
│ │ └── fraudprotection
│ │ ├── CoreConfigMagnesExt.kt
│ │ ├── PayPalDataCollector.kt
│ │ ├── PayPalDataCollectorRequest.kt
│ │ ├── SharedPreferenceUtils.kt
│ │ └── UUIDHelper.kt
│ └── test
│ └── java
│ └── com
│ └── paypal
│ └── android
│ └── fraudprotection
│ ├── PayPalDataCollectorUnitTest.kt
│ └── UUIDHelperUnitTest.kt
├── LICENSE
├── MOBILE_CHECKOUT_MIGRATION_GUIDE.md
├── PayPalWebPayments
├── .gitignore
├── build.gradle
├── consumer-rules.pro
├── proguard-rules.pro
└── src
│ ├── main
│ ├── AndroidManifest.xml
│ └── java
│ │ └── com
│ │ └── paypal
│ │ └── android
│ │ └── paypalwebpayments
│ │ ├── PayPalPresentAuthChallengeResult.kt
│ │ ├── PayPalWebCheckoutClient.kt
│ │ ├── PayPalWebCheckoutFinishStartResult.kt
│ │ ├── PayPalWebCheckoutFinishVaultResult.kt
│ │ ├── PayPalWebCheckoutFundingSource.kt
│ │ ├── PayPalWebCheckoutRequest.kt
│ │ ├── PayPalWebLauncher.kt
│ │ ├── PayPalWebVaultRequest.kt
│ │ ├── analytics
│ │ ├── CheckoutEvent.kt
│ │ ├── PayPalWebAnalytics.kt
│ │ └── VaultEvent.kt
│ │ └── errors
│ │ ├── PayPalWebCheckoutError.kt
│ │ └── PayPalWebCheckoutErrorCode.kt
│ └── test
│ ├── java
│ └── com
│ │ └── paypal
│ │ └── android
│ │ └── paypalwebpayments
│ │ ├── PayPalWebCheckoutClientUnitTest.kt
│ │ ├── PayPalWebCheckoutRequestUnitTest.kt
│ │ └── PayPalWebLauncherUnitTest.kt
│ └── resources
│ └── robolectric.properties
├── PaymentButtons
├── build.gradle
├── consumer-rules.pro
├── proguard-rules.pro
└── src
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── com
│ │ └── paypal
│ │ └── android
│ │ └── paymentbuttons
│ │ ├── PayLaterButton.kt
│ │ ├── PayPalButton.kt
│ │ ├── PayPalCreditButton.kt
│ │ ├── PaymentButton.kt
│ │ ├── PaymentButtonColor.kt
│ │ ├── PaymentButtonShape.kt
│ │ ├── PaymentButtonSize.kt
│ │ └── error
│ │ └── Exceptions.kt
│ └── res
│ ├── color
│ ├── paypal_black.xml
│ ├── paypal_blue.xml
│ ├── paypal_dark_blue.xml
│ ├── paypal_gold.xml
│ ├── paypal_silver.xml
│ └── paypal_white.xml
│ ├── drawable
│ ├── logo_paypal_color.xml
│ ├── logo_paypal_monochrome.xml
│ ├── wordmark_paypal_color.xml
│ ├── wordmark_paypal_credit_color.xml
│ ├── wordmark_paypal_credit_monochrome.xml
│ └── wordmark_paypal_monochrome.xml
│ ├── font
│ └── paypalopen_regular.otf
│ ├── layout
│ └── paypal_ui_payment_button_view.xml
│ └── values
│ ├── attrs.xml
│ ├── colors.xml
│ ├── dimens.xml
│ └── strings.xml
├── README.md
├── Venmo
├── build.gradle
├── consumer-rules.pro
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── paypal
│ │ └── android
│ │ └── venmo
│ │ └── ExampleInstrumentedTest.kt
│ ├── main
│ └── AndroidManifest.xml
│ └── test
│ └── java
│ └── com
│ └── paypal
│ └── android
│ └── venmo
│ └── ExampleUnitTest.kt
├── adr
├── 1-remove-graphql-generics
│ ├── figure-graph-ql-client.png
│ ├── figure-payments-sdk-architecture.png
│ ├── figure-query-abstract-base-class.png
│ └── index.md
└── 2-remove-core-api-and-http-request-factories
│ ├── figure-card-client-example.png
│ ├── figure-deep-module-vs-shallow-module.png
│ ├── figure-multi-api-uml.png
│ └── index.md
├── build.gradle
├── codecov.yml
├── detekt
└── detekt-config.yml
├── gradle.properties
├── gradle
├── gradle-publish.gradle
├── libs.versions.toml
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── lint.xml
├── settings.gradle
└── v2_MIGRATION_GUIDE.md
/.github/CODEOWNERS:
--------------------------------------------------------------------------------
1 | * @paypal/sdks-android
2 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.yml:
--------------------------------------------------------------------------------
1 | name: Bug report
2 | description: Create a report to help us improve
3 | body:
4 | - type: input
5 | attributes:
6 | label: PayPal Android SDK Version
7 | placeholder: "e.g. 1.12.0"
8 | validations:
9 | required: true
10 | - type: dropdown
11 | attributes:
12 | label: Environment
13 | options:
14 | - Sandbox
15 | - Live
16 | - Both
17 | validations:
18 | required: true
19 | - type: input
20 | attributes:
21 | label: Android Version & Device
22 | placeholder: "e.g Google Pixel 6a - Android 12.0"
23 | validations:
24 | required: false
25 | - type: textarea
26 | attributes:
27 | label: PayPal dependencies
28 | placeholder: |
29 |
30 |
31 |
32 | validations:
33 | required: true
34 | - type: textarea
35 | attributes:
36 | label: Describe the bug
37 | description: Description of what the bug is. Please include as many details as possible.
38 | validations:
39 | required: true
40 | - type: textarea
41 | attributes:
42 | label: To reproduce
43 | description: Steps to reproduce the behavior.
44 | placeholder: |
45 | 1. Go to '...'
46 | 2. Click on '....'
47 | 3. See error
48 | validations:
49 | required: true
50 | - type: textarea
51 | attributes:
52 | label: Expected behavior
53 | description: Description of what you expected to happen.
54 | validations:
55 | required: true
56 | - type: textarea
57 | attributes:
58 | label: Screenshots
59 | description: If applicable, add screenshots to help explain your problem.
60 | placeholder: |
61 | Do not reveal sensitive data. For example, credit card numbers & customer credentials.
62 | validations:
63 | required: false
64 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/config.yml:
--------------------------------------------------------------------------------
1 | blank_issues_enabled: false
2 | contact_links:
3 | - name: Developer Support
4 | url: https://www.paypal-support.com/
5 | about: If you need help troubleshooting your integration, reach out to PayPal Support.
6 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.yml:
--------------------------------------------------------------------------------
1 | name: Feature request
2 | description: Suggest an idea for our SDK
3 | body:
4 | - type: textarea
5 | attributes:
6 | label: Is your feature request related to a problem? Please describe.
7 | description: A clear and concise description of what the problem is.
8 | validations:
9 | required: false
10 | - type: textarea
11 | attributes:
12 | label: Describe the solution you'd like.
13 | description: Description of what you want to happen.
14 | placeholder: |
15 | Follow the [user story](https://en.wikipedia.org/wiki/User_story) format to clearly describe the use case.
16 | validations:
17 | required: false
18 |
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | Thank you for your contribution to PayPal.
2 |
3 | > Before submitting this PR, note that we cannot accept language translation PRs. We have a dedicated localization team to provide the translations. If there is an error in a specific translation, you may open an issue and we will escalate it to the localization team.
4 |
5 | ### Summary of changes
6 |
7 | -
8 |
9 | ### Checklist
10 |
11 | - [ ] Added a changelog entry
12 |
13 | ### Authors
14 | > List GitHub usernames for everyone who contributed to this pull request.
15 |
16 | -
17 |
--------------------------------------------------------------------------------
/.github/actions/decode_signing_key_action/action.yml:
--------------------------------------------------------------------------------
1 | name: Decode signing key
2 | description: 'Decodes gpg key into file'
3 | inputs:
4 | signing_key_file:
5 | description: 'Signing key file'
6 | required: true
7 | default: ''
8 | signing_file_path:
9 | description: 'Signing file path'
10 | required: true
11 | default: ''
12 | runs:
13 | using: "composite"
14 | steps:
15 | - run: |
16 | echo "${{inputs.signing_key_file}}" > ~/secretKey.gpg.b64
17 | base64 -d ~/secretKey.gpg.b64 > ${{ inputs.signing_file_path }}
18 | shell: bash
--------------------------------------------------------------------------------
/.github/actions/publish_all_modules/action.yml:
--------------------------------------------------------------------------------
1 | name: 'Publish All Modules'
2 | description: 'Publishes all modules'
3 | inputs:
4 | sonatype_usr:
5 | description: 'Sonatype user'
6 | required: true
7 | default: ''
8 | sonatype_pwd:
9 | description: 'Sonatype password'
10 | required: true
11 | default: ''
12 | signing_key_id:
13 | description: 'Signing key id'
14 | required: true
15 | default: ''
16 | signing_key_pwd:
17 | description: 'Signing key password'
18 | required: true
19 | default: ''
20 | signing_key_file:
21 | description: 'Signing key file'
22 | required: true
23 | default: ''
24 | runs:
25 | using: "composite"
26 | steps:
27 | - run: |
28 | ./gradlew --stacktrace clean publishToSonatype closeAndReleaseSonatypeStagingRepository
29 | shell: bash
30 | env:
31 | SONATYPE_NEXUS_USERNAME: ${{ inputs.sonatype_usr }}
32 | SONATYPE_NEXUS_PASSWORD: ${{ inputs.sonatype_pwd }}
33 | SIGNING_KEY_ID: ${{ inputs.signing_key_id }}
34 | SIGNING_KEY_PASSWORD: ${{ inputs.signing_key_pwd }}
35 | SIGNING_KEY_FILE: ${{ inputs.signing_key_file }}
--------------------------------------------------------------------------------
/.github/actions/set_github_user/action.yml:
--------------------------------------------------------------------------------
1 | name: Set github user
2 | description: 'Sets github user for a particular workflow'
3 | runs:
4 | using: "composite"
5 | steps:
6 | - run: |
7 | git config user.name paypalsdk
8 | git config user.email sdks@paypal.com
9 | shell: bash
--------------------------------------------------------------------------------
/.github/actions/unit_test_module/action.yml:
--------------------------------------------------------------------------------
1 | name: Unit Test Module
2 | description: 'Runs unit test for specified module'
3 | inputs:
4 | module:
5 | description: 'Module'
6 | required: true
7 | default: ''
8 | runs:
9 | using: "composite"
10 | steps:
11 | - run: ./gradlew --stacktrace :${{ inputs.module }}:testRelease
12 | shell: bash
--------------------------------------------------------------------------------
/.github/workflows/build.yml:
--------------------------------------------------------------------------------
1 | name: Build
2 | on: [pull_request, workflow_dispatch]
3 | concurrency:
4 | group: build-${{ github.event.number }}
5 | cancel-in-progress: true
6 | jobs:
7 | build_aar:
8 | name: Build
9 | runs-on: ubuntu-latest
10 | env:
11 | SIGNING_KEY_FILE_PATH: /home/runner/secretKey.gpg
12 | steps:
13 | - name: Checkout Repository
14 | uses: actions/checkout@v4
15 | - name: Set up Java
16 | uses: actions/setup-java@v4
17 | with:
18 | java-version: '17'
19 | distribution: 'microsoft'
20 | #After decoding the secret key, place the file in ~ /. Gradle/ secring.gpg
21 | - name: Decode Signing Key
22 | uses: ./.github/actions/decode_signing_key_action
23 | with:
24 | signing_key_file: ${{ secrets.SIGNING_KEY_FILE }}
25 | signing_file_path: ${{ env.SIGNING_KEY_FILE_PATH }}
26 | - name: Assemble
27 | run: ./gradlew --stacktrace assemble -x :Demo:assemble # we exclude Demo module in assemble
28 | env:
29 | SIGNING_KEY_ID: ${{ secrets.SIGNING_KEY_ID }}
30 | SIGNING_KEY_PASSWORD: ${{ secrets.SIGNING_KEY_PASSWORD }}
31 | SIGNING_KEY_FILE: ${{ env.SIGNING_KEY_FILE_PATH }}
32 |
--------------------------------------------------------------------------------
/.github/workflows/release_demo.yml:
--------------------------------------------------------------------------------
1 | name: Release Demo
2 | on: workflow_dispatch
3 | env:
4 | DEMO_KEYSTORE_FILE: /home/runner/demo_keystore.keystore
5 | DEMO_GCP_SERVICE_ACCOUNT_CREDENTIALS_FILE: /home/runner/demo_gcp_service_account_credentials.json
6 | jobs:
7 | publish_demo_app:
8 | name: Publish Demo App
9 | runs-on: ubuntu-latest
10 | steps:
11 | - name: Checkout Repository
12 | uses: actions/checkout@v4
13 | - name: Set up Java
14 | uses: actions/setup-java@v4
15 | with:
16 | java-version: '17'
17 | distribution: 'microsoft'
18 | - name: Decode Demo Keystore
19 | run: |
20 | echo "${{ secrets.DEMO_KEYSTORE_BASE64_ENCODED }}" > ~/demo_keystore.keystore.b64
21 | base64 -d ~/demo_keystore.keystore.b64 > "${DEMO_KEYSTORE_FILE}"
22 | - name: Decode Demo GCP Service Account Credentials
23 | run: |
24 | echo "${{ secrets.DEMO_GCP_SERVICE_ACCOUNT_CREDENTIALS_BASE64_ENCODED }}" > ~/gcp_service_account_credentials.json.b64
25 | base64 -d ~/gcp_service_account_credentials.json.b64 > "${DEMO_GCP_SERVICE_ACCOUNT_CREDENTIALS_FILE}"
26 | - name: Publish Release Bundle
27 | run: ./gradlew publishReleaseBundle
28 | env:
29 | DEMO_KEYSTORE_PASSWORD: ${{ secrets.DEMO_KEYSTORE_PASSWORD }}
30 | DEMO_KEY_ALIAS: ${{ secrets.DEMO_KEY_ALIAS }}
31 | DEMO_KEY_PASSWORD: ${{ secrets.DEMO_KEY_PASSWORD }}
32 | bump_demo_app_version_code:
33 | needs: [ publish_demo_app ]
34 | name: Bump Demo App Version
35 | runs-on: ubuntu-latest
36 | steps:
37 | - name: Checkout Repository
38 | uses: actions/checkout@v4
39 | - name: Set github user
40 | uses: ./.github/actions/set_github_user
41 | - name: Update Version
42 | run: |
43 | VERSION_CODE=$(./gradlew -q getDemoAppVersionCode)
44 | UPDATED_VERSION_CODE=$((${VERSION_CODE} + 1))
45 |
46 | ./gradlew -PdemoAppVersionCodeParam=${UPDATED_VERSION_CODE} setDemoAppVersionCode
47 |
48 | git add .
49 | git commit -m "Bump demo app version code to ${UPDATED_VERSION_CODE}."
50 | git push origin HEAD
--------------------------------------------------------------------------------
/.github/workflows/tests.yml:
--------------------------------------------------------------------------------
1 | name: Tests
2 | on: [ pull_request, workflow_dispatch ]
3 | concurrency:
4 | group: tests-${{ github.event.number }}
5 | cancel-in-progress: true
6 | jobs:
7 | unit_test_job:
8 | name: Unit Tests
9 | runs-on: ubuntu-latest
10 | steps:
11 | - name: Checkout Repository
12 | uses: actions/checkout@v4
13 | - name: Set up Java
14 | uses: actions/setup-java@v4
15 | with:
16 | java-version: '17'
17 | distribution: 'microsoft'
18 | - name: Run Unit Tests
19 | run: ./gradlew --stacktrace testDebug
20 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Built application files
2 | *.apk
3 | *.aar
4 | *.ap_
5 | *.aab
6 |
7 | # Files for the ART/Dalvik VM
8 | *.dex
9 |
10 | # Java class files
11 | *.class
12 |
13 | # Generated files
14 | bin/
15 | gen/
16 | out/
17 | # Uncomment the following line in case you need and you don't have the release build type files in your app
18 | # release/
19 |
20 | # Gradle files
21 | .gradle/
22 | build/
23 |
24 | # Local configuration file (sdk path, etc)
25 | local.properties
26 |
27 | # Proguard folder generated by Eclipse
28 | proguard/
29 |
30 | # Log Files
31 | *.log
32 |
33 | # Android Studio Navigation editor temp files
34 | .navigation/
35 |
36 | # Android Studio captures folder
37 | captures/
38 |
39 | # Android Studio 3 in .gitignore file.
40 | .idea/
41 |
42 |
43 | # Keystore files
44 | # Uncomment the following lines if you do not want to check your keystore files in.
45 | #*.jks
46 | #*.keystore
47 |
48 | # External native build folder generated in Android Studio 2.2 and later
49 | .externalNativeBuild
50 | .cxx/
51 |
52 | # Google Services (e.g. APIs or Firebase)
53 | # google-services.json
54 |
55 | # Freeline
56 | freeline.py
57 | freeline/
58 | freeline_project_description.json
59 |
60 | # fastlane
61 | fastlane/report.xml
62 | fastlane/Preview.html
63 | fastlane/screenshots
64 | fastlane/test_output
65 | fastlane/readme.md
66 |
67 | # Version control
68 | vcs.xml
69 |
70 | # lint
71 | lint/intermediates/
72 | lint/generated/
73 | lint/outputs/
74 | lint/tmp/
75 | # lint/reports/
76 |
77 | # Android Profiling
78 | *.hprof
79 |
80 | # DS_Store
81 | .DS_Store
82 |
83 | # PayPal
84 | paypal.properties
85 |
86 | # vim
87 | *.swp
88 |
--------------------------------------------------------------------------------
/.idea/.gitignore:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/paypal/paypal-android/9d77a9e760518e586a64c135ad7f225e4293fb20/.idea/.gitignore
--------------------------------------------------------------------------------
/.idea/.name:
--------------------------------------------------------------------------------
1 | AndroidSDK
--------------------------------------------------------------------------------
/.idea/compiler.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/CardPayments/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/CardPayments/consumer-rules.pro:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/paypal/paypal-android/9d77a9e760518e586a64c135ad7f225e4293fb20/CardPayments/consumer-rules.pro
--------------------------------------------------------------------------------
/CardPayments/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
--------------------------------------------------------------------------------
/CardPayments/src/androidTest/java/com/paypal/android/cardpayments/ExampleInstrumentedTest.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android.cardpayments
2 |
3 | import androidx.test.platform.app.InstrumentationRegistry
4 | import androidx.test.ext.junit.runners.AndroidJUnit4
5 | import junit.framework.TestCase.assertEquals
6 |
7 | import org.junit.Test
8 | import org.junit.runner.RunWith
9 |
10 | /**
11 | * Instrumented test, which will execute on an Android device.
12 | *
13 | * See [testing documentation](http://d.android.com/tools/testing).
14 | */
15 | @RunWith(AndroidJUnit4::class)
16 | class ExampleInstrumentedTest {
17 | @Test
18 | fun useAppContext() {
19 | // Context of the app under test.
20 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext
21 | assertEquals("com.paypal.android.card.test", appContext.packageName)
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/CardPayments/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/CardPayments/src/main/java/com/paypal/android/cardpayments/ApproveOrderMetadata.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android.cardpayments
2 |
3 | import com.paypal.android.cardpayments.model.PaymentSource
4 | import com.paypal.android.corepayments.PaymentsJSON
5 | import org.json.JSONObject
6 |
7 | internal data class ApproveOrderMetadata(
8 | val orderId: String,
9 | val paymentSource: PaymentSource? = null
10 | ) {
11 |
12 | companion object {
13 |
14 | const val KEY_ORDER_ID = "order_id"
15 | const val KEY_PAYMENT_SOURCE = "payment_source"
16 |
17 | fun fromJSON(data: JSONObject?): ApproveOrderMetadata? =
18 | data?.let { fromJSON(PaymentsJSON(it)) }
19 |
20 | private fun fromJSON(json: PaymentsJSON): ApproveOrderMetadata {
21 | val orderId = json.getString(KEY_ORDER_ID)
22 | val paymentSource = json.optMapObject(KEY_PAYMENT_SOURCE) { PaymentSource(it) }
23 | return ApproveOrderMetadata(orderId, paymentSource)
24 | }
25 | }
26 |
27 | fun toJSON(): JSONObject =
28 | JSONObject()
29 | .put(KEY_ORDER_ID, orderId)
30 | .putOpt(KEY_PAYMENT_SOURCE, paymentSource?.toJSON())
31 | }
32 |
--------------------------------------------------------------------------------
/CardPayments/src/main/java/com/paypal/android/cardpayments/Card.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android.cardpayments
2 |
3 | import android.os.Parcelable
4 | import com.paypal.android.corepayments.Address
5 | import kotlinx.parcelize.Parcelize
6 |
7 | /**
8 | * Represents raw credit or debit card data provided by the customer.
9 | */
10 | @Parcelize
11 | data class Card @JvmOverloads constructor(
12 |
13 | /**
14 | * The primary account number (PAN) for the payment card
15 | */
16 | var number: String,
17 |
18 | /**
19 | * The 2-digit card expiration month in `MM` format
20 | */
21 | val expirationMonth: String,
22 |
23 | /**
24 | * The 4-digit card expiration year in `YYYY` format
25 | */
26 | val expirationYear: String,
27 |
28 | /**
29 | * The three- or four-digit security code of the card. Also known as the CVV, CVC, CVN, CVE, or CID.
30 | */
31 | var securityCode: String,
32 |
33 | /**
34 | * Optional. The card holder's name as it appears on the card
35 | */
36 | var cardholderName: String? = null,
37 |
38 | /**
39 | * Optional. The billing address
40 | */
41 | var billingAddress: Address? = null,
42 | ) : Parcelable
43 |
--------------------------------------------------------------------------------
/CardPayments/src/main/java/com/paypal/android/cardpayments/CardApproveOrderCallback.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android.cardpayments
2 |
3 | import androidx.annotation.MainThread
4 |
5 | fun interface CardApproveOrderCallback {
6 | /**
7 | * Called when the order is approved.
8 | *
9 | * @param result [CardApproveOrderResult] result with details
10 | */
11 | @MainThread
12 | fun onCardApproveOrderResult(result: CardApproveOrderResult)
13 | }
14 |
--------------------------------------------------------------------------------
/CardPayments/src/main/java/com/paypal/android/cardpayments/CardApproveOrderResult.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android.cardpayments
2 |
3 | import com.paypal.android.corepayments.PayPalSDKError
4 |
5 | sealed class CardApproveOrderResult {
6 |
7 | data class Success(
8 | val orderId: String,
9 | val status: String? = null,
10 | val didAttemptThreeDSecureAuthentication: Boolean = false
11 | ) : CardApproveOrderResult()
12 |
13 | data class AuthorizationRequired(val authChallenge: CardAuthChallenge) : CardApproveOrderResult()
14 | data class Failure(val error: PayPalSDKError) : CardApproveOrderResult()
15 | }
16 |
--------------------------------------------------------------------------------
/CardPayments/src/main/java/com/paypal/android/cardpayments/CardAuthChallenge.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android.cardpayments
2 |
3 | import android.net.Uri
4 | import android.os.Parcelable
5 | import kotlinx.parcelize.Parcelize
6 |
7 | /**
8 | * Pass this object to [CardClient.presentAuthChallenge] to present an authentication challenge
9 | * that was received in response to a [CardClient.approveOrder] or [CardClient.vault] call.
10 | */
11 | sealed class CardAuthChallenge {
12 | // Ref: https://stackoverflow.com/a/44420084
13 | internal abstract val url: Uri
14 | internal abstract val returnUrlScheme: String?
15 |
16 | @Parcelize
17 | internal class ApproveOrder(
18 | override val url: Uri,
19 | val request: CardRequest,
20 | override val returnUrlScheme: String? = Uri.parse(request.returnUrl).scheme
21 | ) : CardAuthChallenge(), Parcelable
22 |
23 | @Parcelize
24 | internal class Vault(
25 | override val url: Uri,
26 | val request: CardVaultRequest,
27 | override val returnUrlScheme: String? = Uri.parse(request.returnUrl).scheme
28 | ) : CardAuthChallenge(), Parcelable
29 | }
30 |
--------------------------------------------------------------------------------
/CardPayments/src/main/java/com/paypal/android/cardpayments/CardError.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android.cardpayments
2 |
3 | import com.paypal.android.corepayments.PayPalSDKError
4 | import com.paypal.android.corepayments.graphql.GraphQLError
5 |
6 | internal object CardError {
7 |
8 | // 0. An error from 3DS verification
9 | val threeDSVerificationError = PayPalSDKError(
10 | code = CardErrorCode.THREEDS_VERIFICATION_FAILED.ordinal,
11 | errorDescription = "3DS Verification is returning an error."
12 | )
13 |
14 | // 1.
15 | val malformedDeepLinkError = PayPalSDKError(
16 | code = CardErrorCode.MALFORMED_DEEPLINK_URL.ordinal,
17 | errorDescription = "Malformed deeplink URL."
18 | )
19 |
20 | // 2. An unknown error occurred.
21 | val unknownError = PayPalSDKError(
22 | code = CardErrorCode.UNKNOWN.ordinal,
23 | errorDescription = "An unknown error occurred. Contact developer.paypal.com/support."
24 | )
25 |
26 | // 3. An unknown error occurred.
27 | fun browserSwitchError(cause: Exception) = PayPalSDKError(
28 | code = CardErrorCode.BROWSER_SWITCH.ordinal,
29 | errorDescription = cause.message ?: "Unable to Browser Switch"
30 | )
31 |
32 | fun updateSetupTokenResponseBodyMissing(errors: List?, correlationId: String?) = PayPalSDKError(
33 | 0,
34 | "Error updating setup token: $errors",
35 | correlationId
36 | )
37 | }
38 |
--------------------------------------------------------------------------------
/CardPayments/src/main/java/com/paypal/android/cardpayments/CardErrorCode.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android.cardpayments
2 |
3 | internal enum class CardErrorCode {
4 | THREEDS_VERIFICATION_FAILED,
5 | MALFORMED_DEEPLINK_URL,
6 | UNKNOWN,
7 | BROWSER_SWITCH
8 | }
9 |
--------------------------------------------------------------------------------
/CardPayments/src/main/java/com/paypal/android/cardpayments/CardFinishApproveOrderResult.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android.cardpayments
2 |
3 | import com.paypal.android.corepayments.PayPalSDKError
4 |
5 | sealed class CardFinishApproveOrderResult {
6 | data class Success(
7 | val orderId: String,
8 | val status: String? = null,
9 | val didAttemptThreeDSecureAuthentication: Boolean = false
10 | ) : CardFinishApproveOrderResult()
11 |
12 | data class Failure(val error: PayPalSDKError) : CardFinishApproveOrderResult()
13 | data object Canceled : CardFinishApproveOrderResult()
14 | data object NoResult : CardFinishApproveOrderResult()
15 | }
16 |
--------------------------------------------------------------------------------
/CardPayments/src/main/java/com/paypal/android/cardpayments/CardFinishVaultResult.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android.cardpayments
2 |
3 | import com.paypal.android.corepayments.PayPalSDKError
4 |
5 | sealed class CardFinishVaultResult {
6 |
7 | data class Success(
8 | val setupTokenId: String,
9 | val status: String? = null,
10 | val didAttemptThreeDSecureAuthentication: Boolean = false
11 | ) : CardFinishVaultResult()
12 |
13 | data class Failure(val error: PayPalSDKError) : CardFinishVaultResult()
14 |
15 | data object Canceled : CardFinishVaultResult()
16 | data object NoResult : CardFinishVaultResult()
17 | }
18 |
--------------------------------------------------------------------------------
/CardPayments/src/main/java/com/paypal/android/cardpayments/CardPresentAuthChallengeResult.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android.cardpayments
2 |
3 | import com.paypal.android.corepayments.PayPalSDKError
4 |
5 | sealed class CardPresentAuthChallengeResult {
6 | data class Success(val authState: String) : CardPresentAuthChallengeResult()
7 | data class Failure(val error: PayPalSDKError) : CardPresentAuthChallengeResult()
8 | }
9 |
--------------------------------------------------------------------------------
/CardPayments/src/main/java/com/paypal/android/cardpayments/CardRequest.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android.cardpayments
2 |
3 | import android.os.Parcelable
4 | import com.paypal.android.cardpayments.threedsecure.SCA
5 | import kotlinx.parcelize.Parcelize
6 |
7 | /**
8 | * A card request to process a card payment.
9 | *
10 | * @property orderId The order ID to to process the payment
11 | * @property card Card used for payment
12 | * @property returnUrl Url to return to app after SCA challenge finishes
13 | * @property sca Specify to always launch 3DS or only when required. Defaults to `SCA.SCA_WHEN_REQUIRED`.
14 | */
15 | @Parcelize
16 | data class CardRequest @JvmOverloads constructor(
17 | val orderId: String,
18 | val card: Card,
19 | val returnUrl: String,
20 | val sca: SCA = SCA.SCA_WHEN_REQUIRED,
21 | ) : Parcelable
22 |
--------------------------------------------------------------------------------
/CardPayments/src/main/java/com/paypal/android/cardpayments/CardRequestFactory.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android.cardpayments
2 |
3 | import com.paypal.android.corepayments.APIRequest
4 | import com.paypal.android.corepayments.HttpMethod
5 | import org.json.JSONObject
6 |
7 | internal class CardRequestFactory {
8 |
9 | fun createConfirmPaymentSourceRequest(cardRequest: CardRequest): APIRequest {
10 | val card = cardRequest.card
11 | val cardNumber = card.number.replace("\\s".toRegex(), "")
12 | val cardExpiry = "${card.expirationYear}-${card.expirationMonth}"
13 |
14 | val cardJSON = JSONObject()
15 | .put("number", cardNumber)
16 | .put("expiry", cardExpiry)
17 |
18 | card.cardholderName?.let { cardJSON.put("name", it) }
19 | cardJSON.put("security_code", card.securityCode)
20 |
21 | card.billingAddress?.apply {
22 | val billingAddressJSON = JSONObject()
23 | .put("address_line_1", streetAddress)
24 | .put("address_line_2", extendedAddress)
25 | .put("admin_area_1", region)
26 | .put("admin_area_2", locality)
27 | .put("postal_code", postalCode)
28 | .put("country_code", countryCode)
29 | cardJSON.put("billing_address", billingAddressJSON)
30 | }
31 |
32 | val bodyJSON = JSONObject()
33 | val verificationJSON = JSONObject()
34 | .put("method", cardRequest.sca.name)
35 | val attributesJSON = JSONObject()
36 | .put("verification", verificationJSON)
37 |
38 | cardJSON.put("attributes", attributesJSON)
39 |
40 | val returnUrl = cardRequest.returnUrl
41 | val returnURLJSON = JSONObject()
42 | .put("return_url", returnUrl)
43 | .put("cancel_url", returnUrl) // we can set the same url
44 | bodyJSON.put("application_context", returnURLJSON)
45 |
46 | val paymentSourceJSON = JSONObject().put("card", cardJSON)
47 | bodyJSON.put("payment_source", paymentSourceJSON)
48 |
49 | val body = bodyJSON.toString().replace("\\/", "/")
50 |
51 | val path = "v2/checkout/orders/${cardRequest.orderId}/confirm-payment-source"
52 | return APIRequest(path, HttpMethod.POST, body)
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/CardPayments/src/main/java/com/paypal/android/cardpayments/CardVaultCallback.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android.cardpayments
2 |
3 | import androidx.annotation.MainThread
4 |
5 | fun interface CardVaultCallback {
6 |
7 | /**
8 | * Called when a successful vault has occurred.
9 | */
10 | @MainThread
11 | fun onCardVaultResult(result: CardVaultResult)
12 | }
13 |
--------------------------------------------------------------------------------
/CardPayments/src/main/java/com/paypal/android/cardpayments/CardVaultRequest.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android.cardpayments
2 |
3 | import android.os.Parcelable
4 | import kotlinx.parcelize.Parcelize
5 |
6 | /**
7 | * @suppress
8 | *
9 | * A vault request to attach a payment method to a setup token.
10 | *
11 | * @property setupTokenId id for the setup token to update.
12 | * @property card card payment source to attach to the setup token.
13 | * @property returnUrl return url for deep linking back into the merchant app after an auth challenge
14 | */
15 | @Parcelize
16 | data class CardVaultRequest(
17 | val setupTokenId: String,
18 | val card: Card,
19 | val returnUrl: String? = "",
20 | ) : Parcelable
21 |
--------------------------------------------------------------------------------
/CardPayments/src/main/java/com/paypal/android/cardpayments/CardVaultResult.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android.cardpayments
2 |
3 | import com.paypal.android.corepayments.PayPalSDKError
4 |
5 | sealed class CardVaultResult {
6 |
7 | /**
8 | * A result returned by [CardClient] when an a successful vault occurs.
9 | *
10 | * @param setupTokenId the id for the setup token that was recently updated
11 | * @param status the status of the updated setup token
12 | * @property [didAttemptThreeDSecureAuthentication] 3DS verification was attempted.
13 | * Use v2/checkout/orders/{orderId} in your server to get verification results.
14 | */
15 | data class Success(
16 | val setupTokenId: String,
17 | val status: String? = null,
18 | val didAttemptThreeDSecureAuthentication: Boolean = false
19 | ) : CardVaultResult()
20 |
21 | data class AuthorizationRequired(val authChallenge: CardAuthChallenge) : CardVaultResult()
22 | data class Failure(val error: PayPalSDKError) : CardVaultResult()
23 | }
24 |
--------------------------------------------------------------------------------
/CardPayments/src/main/java/com/paypal/android/cardpayments/UpdateSetupTokenResult.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android.cardpayments
2 |
3 | import com.paypal.android.corepayments.PayPalSDKError
4 |
5 | internal sealed class UpdateSetupTokenResult {
6 |
7 | data class Success(
8 | val setupTokenId: String,
9 | val status: String,
10 | val approveHref: String?
11 | ) : UpdateSetupTokenResult()
12 |
13 | data class Failure(val error: PayPalSDKError) : UpdateSetupTokenResult()
14 | }
15 |
--------------------------------------------------------------------------------
/CardPayments/src/main/java/com/paypal/android/cardpayments/analytics/ApproveOrderEvent.kt:
--------------------------------------------------------------------------------
1 | @file:Suppress("SpacingAroundParens", "NoMultipleSpaces", "MaxLineLength")
2 |
3 | package com.paypal.android.cardpayments.analytics
4 |
5 | internal enum class ApproveOrderEvent(val value: String) {
6 | // @formatter:off
7 | STARTED( "card-payments:approve-order:started"),
8 | SUCCEEDED("card-payments:approve-order:succeeded"),
9 | FAILED( "card-payments:approve-order:failed"),
10 |
11 | AUTH_CHALLENGE_REQUIRED("card-payments:approve-order:auth-challenge-required"),
12 |
13 | AUTH_CHALLENGE_PRESENTATION_SUCCEEDED("card-payments:approve-order:auth-challenge-presentation:succeeded"),
14 | AUTH_CHALLENGE_PRESENTATION_FAILED( "card-payments:approve-order:auth-challenge-presentation:failed"),
15 |
16 | AUTH_CHALLENGE_SUCCEEDED("card-payments:approve-order:auth-challenge:succeeded"),
17 | AUTH_CHALLENGE_FAILED( "card-payments:approve-order:auth-challenge:failed"),
18 | AUTH_CHALLENGE_CANCELED( "card-payments:approve-order:auth-challenge:canceled"),
19 | // @formatter:on
20 | }
21 |
--------------------------------------------------------------------------------
/CardPayments/src/main/java/com/paypal/android/cardpayments/analytics/CardAnalytics.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android.cardpayments.analytics
2 |
3 | import com.paypal.android.corepayments.analytics.AnalyticsService
4 |
5 | internal class CardAnalytics(private val analyticsService: AnalyticsService) {
6 |
7 | fun notify(event: ApproveOrderEvent, orderId: String?) {
8 | analyticsService.sendAnalyticsEvent(event.value, orderId)
9 | }
10 |
11 | fun notify(event: VaultEvent, setupTokenId: String?) {
12 | analyticsService.sendAnalyticsEvent(event.value, setupTokenId)
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/CardPayments/src/main/java/com/paypal/android/cardpayments/analytics/VaultEvent.kt:
--------------------------------------------------------------------------------
1 | @file:Suppress("SpacingAroundParens", "NoMultipleSpaces", "MaxLineLength")
2 |
3 | package com.paypal.android.cardpayments.analytics
4 |
5 | internal enum class VaultEvent(val value: String) {
6 | // @formatter:off
7 | STARTED( "card-payments:vault-wo-purchase:started"),
8 | SUCCEEDED("card-payments:vault-wo-purchase:succeeded"),
9 | FAILED( "card-payments:vault-wo-purchase:failed"),
10 |
11 | AUTH_CHALLENGE_REQUIRED("card-payments:vault-wo-purchase:auth-challenge-required"),
12 |
13 | AUTH_CHALLENGE_PRESENTATION_SUCCEEDED("card-payments:vault-wo-purchase:auth-challenge-presentation:succeeded"),
14 | AUTH_CHALLENGE_PRESENTATION_FAILED( "card-payments:vault-wo-purchase:auth-challenge-presentation:failed"),
15 |
16 | AUTH_CHALLENGE_SUCCEEDED("card-payments:vault-wo-purchase:auth-challenge:succeeded"),
17 | AUTH_CHALLENGE_FAILED( "card-payments:vault-wo-purchase:auth-challenge:failed"),
18 | AUTH_CHALLENGE_CANCELED( "card-payments:vault-wo-purchase:auth-challenge:canceled"),
19 | // @formatter:on
20 | }
21 |
--------------------------------------------------------------------------------
/CardPayments/src/main/java/com/paypal/android/cardpayments/api/CheckoutOrdersAPI.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android.cardpayments.api
2 |
3 | import com.paypal.android.cardpayments.CardRequest
4 | import com.paypal.android.cardpayments.CardRequestFactory
5 | import com.paypal.android.cardpayments.CardResponseParser
6 | import com.paypal.android.corepayments.CoreConfig
7 | import com.paypal.android.corepayments.RestClient
8 |
9 | internal class CheckoutOrdersAPI(
10 | private val restClient: RestClient,
11 | private val requestFactory: CardRequestFactory = CardRequestFactory(),
12 | private val responseParser: CardResponseParser = CardResponseParser()
13 | ) {
14 | constructor(coreConfig: CoreConfig) : this(RestClient(coreConfig))
15 |
16 | suspend fun confirmPaymentSource(cardRequest: CardRequest): ConfirmPaymentSourceResult {
17 | val apiRequest = requestFactory.createConfirmPaymentSourceRequest(cardRequest)
18 | val httpResponse = restClient.send(apiRequest)
19 |
20 | val error = responseParser.parseError(httpResponse)
21 | return if (error == null) {
22 | responseParser.parseConfirmPaymentSourceResponse(httpResponse)
23 | } else {
24 | ConfirmPaymentSourceResult.Failure(error)
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/CardPayments/src/main/java/com/paypal/android/cardpayments/api/ConfirmPaymentSourceResult.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android.cardpayments.api
2 |
3 | import com.paypal.android.cardpayments.model.PaymentSource
4 | import com.paypal.android.cardpayments.model.PurchaseUnit
5 | import com.paypal.android.corepayments.OrderStatus
6 | import com.paypal.android.corepayments.PayPalSDKError
7 |
8 | internal sealed class ConfirmPaymentSourceResult {
9 |
10 | data class Success(
11 | val orderId: String,
12 | val status: OrderStatus? = null,
13 | val payerActionHref: String? = null,
14 | val paymentSource: PaymentSource? = null,
15 | val purchaseUnits: List? = null
16 | ) : ConfirmPaymentSourceResult()
17 |
18 | data class Failure(val error: PayPalSDKError) : ConfirmPaymentSourceResult()
19 | }
20 |
--------------------------------------------------------------------------------
/CardPayments/src/main/java/com/paypal/android/cardpayments/model/Amount.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android.cardpayments.model
2 |
3 | import com.paypal.android.corepayments.PaymentsJSON
4 |
5 | internal data class Amount(
6 | val currencyCode: String?,
7 | val value: String?
8 | ) {
9 | internal constructor(json: PaymentsJSON) : this(
10 | json.optString("currency_code"),
11 | json.optString("value")
12 | )
13 | }
14 |
--------------------------------------------------------------------------------
/CardPayments/src/main/java/com/paypal/android/cardpayments/model/AuthenticationResult.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android.cardpayments.model
2 |
3 | import com.paypal.android.cardpayments.threedsecure.ThreeDSecureResult
4 | import com.paypal.android.corepayments.PaymentsJSON
5 | import org.json.JSONObject
6 |
7 | internal data class AuthenticationResult(
8 | val liabilityShift: String?,
9 | val threeDSecure: ThreeDSecureResult? = null
10 | ) {
11 |
12 | companion object {
13 | const val KEY_LIABILITY_SHIFT = "liability_shift"
14 | const val KEY_THREE_D_SECURE = "three_d_secure"
15 | }
16 |
17 | internal constructor(json: PaymentsJSON) : this(
18 | json.optString(KEY_LIABILITY_SHIFT),
19 | json.optMapObject(KEY_THREE_D_SECURE) { ThreeDSecureResult(it) }
20 | )
21 |
22 | fun toJSON(): JSONObject {
23 | return JSONObject()
24 | .putOpt(KEY_LIABILITY_SHIFT, liabilityShift)
25 | .putOpt(KEY_THREE_D_SECURE, threeDSecure?.toJSON())
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/CardPayments/src/main/java/com/paypal/android/cardpayments/model/Payee.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android.cardpayments.model
2 |
3 | import com.paypal.android.corepayments.PaymentsJSON
4 |
5 | internal data class Payee(val emailAddress: String) {
6 | internal constructor(json: PaymentsJSON) : this(json.optString("email_address") ?: "")
7 | }
8 |
--------------------------------------------------------------------------------
/CardPayments/src/main/java/com/paypal/android/cardpayments/model/PaymentSource.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android.cardpayments.model
2 |
3 | import com.paypal.android.corepayments.PaymentsJSON
4 | import org.json.JSONObject
5 |
6 | internal data class PaymentSource(
7 | val lastDigits: String,
8 | val brand: String,
9 | val type: String? = null,
10 | val authenticationResult: AuthenticationResult? = null
11 | ) {
12 |
13 | companion object {
14 | const val KEY_LAST_DIGITS = "last_digits"
15 | const val KEY_BRAND = "brand"
16 | const val KEY_TYPE = "type"
17 | const val KEY_AUTHENTICATION_RESULT = "authentication_result"
18 | }
19 |
20 | internal constructor(json: PaymentsJSON) : this(
21 | json.getString(KEY_LAST_DIGITS),
22 | json.getString(KEY_BRAND),
23 | json.optString(KEY_TYPE),
24 | json.optMapObject(KEY_AUTHENTICATION_RESULT) { AuthenticationResult(it) }
25 | )
26 |
27 | fun toJSON(): JSONObject {
28 | return JSONObject()
29 | .put(KEY_LAST_DIGITS, lastDigits)
30 | .put(KEY_BRAND, brand)
31 | .putOpt(KEY_TYPE, type)
32 | .putOpt(KEY_AUTHENTICATION_RESULT, authenticationResult?.toJSON())
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/CardPayments/src/main/java/com/paypal/android/cardpayments/model/PurchaseUnit.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android.cardpayments.model
2 |
3 | import com.paypal.android.corepayments.PaymentsJSON
4 |
5 | /**
6 | * A purchase unit for an order. If an order takes multiple purchase units,
7 | * each one must contain a reference id.
8 | */
9 | internal data class PurchaseUnit(
10 | val referenceId: String?,
11 | val amount: Amount? = null,
12 | val payee: Payee? = null
13 | ) {
14 | internal constructor(json: PaymentsJSON) : this(
15 | json.optString("reference_id"),
16 | json.optMapObject("amount") { Amount(it) },
17 | json.optMapObject("payee") { Payee(json) }
18 | )
19 | }
20 |
--------------------------------------------------------------------------------
/CardPayments/src/main/java/com/paypal/android/cardpayments/threedsecure/SCA.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android.cardpayments.threedsecure
2 |
3 | // NEXT MAJOR VERSION: Rename SCA to VerificationMethod
4 | enum class SCA {
5 | SCA_ALWAYS, SCA_WHEN_REQUIRED
6 | }
7 |
--------------------------------------------------------------------------------
/CardPayments/src/main/java/com/paypal/android/cardpayments/threedsecure/ThreeDSecureResult.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android.cardpayments.threedsecure
2 |
3 | import com.paypal.android.corepayments.PaymentsJSON
4 | import org.json.JSONObject
5 |
6 | internal data class ThreeDSecureResult(
7 | val enrollmentStatus: String? = null,
8 | val authenticationStatus: String? = null
9 | ) {
10 |
11 | companion object {
12 | const val KEY_ENROLLMENT_STATUS = "enrollment_status"
13 | const val KEY_AUTHENTICATION_STATUS = "authentication_status"
14 | }
15 |
16 | internal constructor(json: PaymentsJSON) : this(
17 | json.optString(KEY_ENROLLMENT_STATUS),
18 | json.optString(KEY_AUTHENTICATION_STATUS)
19 | )
20 |
21 | fun toJSON(): JSONObject {
22 | return JSONObject()
23 | .putOpt(KEY_ENROLLMENT_STATUS, enrollmentStatus)
24 | .putOpt(KEY_AUTHENTICATION_STATUS, authenticationStatus)
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/CardPayments/src/main/res/raw/graphql_query_update_setup_token.graphql:
--------------------------------------------------------------------------------
1 | mutation UpdateVaultSetupToken(
2 | $clientId: String!
3 | $vaultSetupToken: String!
4 | $paymentSource: PaymentSource
5 | ) {
6 | updateVaultSetupToken(
7 | clientId: $clientId
8 | vaultSetupToken: $vaultSetupToken
9 | paymentSource: $paymentSource
10 | ) {
11 | id,
12 | status,
13 | links {
14 | rel,
15 | href
16 | }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/CardPayments/src/test/java/com/paypal/android/cardpayments/ApproveOrderMetadataUnitTest.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android.cardpayments
2 |
3 | import org.json.JSONObject
4 | import org.junit.Test
5 | import org.skyscreamer.jsonassert.JSONAssert
6 |
7 | class ApproveOrderMetadataUnitTest {
8 |
9 | @Test
10 | fun `it should be able to serialize and deserialize itself in JSON`() {
11 |
12 | // language=JSON
13 | val jsonString = """
14 | {
15 | "order_id": "sample-order-id",
16 | "payment_source": {
17 | "last_digits": "4111",
18 | "brand": "Visa",
19 | "type": "sample-type",
20 | "authentication_result": {
21 | "liability_shift": "YES",
22 | "three_d_secure": {
23 | "enrollment_status": "ENROLLED",
24 | "authentication_status": "AUTHENTICATED"
25 | }
26 | }
27 | }
28 | }
29 | """
30 |
31 | val originalJSON = JSONObject(jsonString)
32 | val reserializedJSON = ApproveOrderMetadata.fromJSON(originalJSON)?.toJSON()
33 |
34 | JSONAssert.assertEquals(originalJSON, reserializedJSON, true)
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/CardPayments/src/test/resources/robolectric.properties:
--------------------------------------------------------------------------------
1 | # TODO: remove this file when Robolectric supports API level 35 (Android 15)
2 | sdk=33
3 |
--------------------------------------------------------------------------------
/CorePayments/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/CorePayments/consumer-rules.pro:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/paypal/paypal-android/9d77a9e760518e586a64c135ad7f225e4293fb20/CorePayments/consumer-rules.pro
--------------------------------------------------------------------------------
/CorePayments/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
--------------------------------------------------------------------------------
/CorePayments/src/androidTest/java/com/paypal/android/corepayments/HttpIntegrationTest.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android.corepayments
2 |
3 | import androidx.test.ext.junit.runners.AndroidJUnit4
4 | import kotlinx.coroutines.ExperimentalCoroutinesApi
5 | import kotlinx.coroutines.test.StandardTestDispatcher
6 | import kotlinx.coroutines.test.runTest
7 | import org.junit.Assert.assertEquals
8 | import org.junit.Test
9 | import org.junit.runner.RunWith
10 | import java.net.URL
11 |
12 | @ExperimentalCoroutinesApi
13 | @RunWith(AndroidJUnit4::class)
14 | class HttpIntegrationTest {
15 |
16 | @Test
17 | fun send_makesAnHttpRequest() = runTest {
18 | val request = HttpRequest(URL("https://www.google.com"))
19 |
20 | val testDispatcher = StandardTestDispatcher(testScheduler)
21 | val sut = Http(testDispatcher)
22 | val result = sut.send(request)
23 | assertEquals(200, result.status)
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/CorePayments/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/CorePayments/src/main/java/com/paypal/android/corepayments/APIRequest.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android.corepayments
2 |
3 | import androidx.annotation.RestrictTo
4 |
5 | /**
6 | * @suppress
7 | */
8 | @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
9 | data class APIRequest(val path: String, val method: HttpMethod, val body: String? = null)
10 |
--------------------------------------------------------------------------------
/CorePayments/src/main/java/com/paypal/android/corepayments/Address.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android.corepayments
2 |
3 | import android.os.Parcelable
4 | import kotlinx.parcelize.Parcelize
5 |
6 | @Parcelize
7 | data class Address(
8 | /**
9 | * The [two-character ISO 3166-1 code], that identifies the country or region.
10 | */
11 | val countryCode: String,
12 |
13 | /**
14 | * Optional. Line 1 of the Address (eg. number, street, etc)
15 | */
16 | val streetAddress: String? = null,
17 |
18 | /**
19 | * Optional. Line 2 of the Address (eg. suite, apt #, etc.)
20 | */
21 | val extendedAddress: String? = null,
22 |
23 | /**
24 | * Optional. City name
25 | */
26 | val locality: String? = null,
27 |
28 | /**
29 | * Optional. Either a two-letter state code (for the US), or an
30 | * ISO-3166-2 country subdivision code of up to three letters.
31 | */
32 | val region: String? = null,
33 |
34 | /**
35 | * Optional. Zip code or equivalent is usually required for countries that have them.
36 | * For a list of countries that do not have postal codes please refer to http://en.wikipedia.org/wiki/Postal_code
37 | */
38 | val postalCode: String? = null
39 | ) : Parcelable
40 |
--------------------------------------------------------------------------------
/CorePayments/src/main/java/com/paypal/android/corepayments/Base64.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android.corepayments
2 |
3 | import android.util.Base64
4 |
5 | internal fun String.base64encoded(): String {
6 | val bytes = toByteArray(Charsets.UTF_8)
7 | return Base64.encodeToString(bytes, Base64.NO_WRAP)
8 | }
9 |
--------------------------------------------------------------------------------
/CorePayments/src/main/java/com/paypal/android/corepayments/BrowserSwitchRequestCodes.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android.corepayments
2 |
3 | object BrowserSwitchRequestCodes {
4 | const val CARD_APPROVE_ORDER = 1
5 | const val CARD_VAULT = 2
6 |
7 | const val PAYPAL_CHECKOUT = 3
8 | const val PAYPAL_VAULT = 4
9 | }
10 |
--------------------------------------------------------------------------------
/CorePayments/src/main/java/com/paypal/android/corepayments/CoreConfig.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android.corepayments
2 |
3 | data class CoreConfig @JvmOverloads constructor(
4 | val clientId: String,
5 | val environment: Environment = Environment.SANDBOX,
6 | )
7 |
--------------------------------------------------------------------------------
/CorePayments/src/main/java/com/paypal/android/corepayments/CoreCoroutineExceptionHandler.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android.corepayments
2 |
3 | import androidx.annotation.RestrictTo
4 | import kotlinx.coroutines.CoroutineExceptionHandler
5 | import kotlin.coroutines.AbstractCoroutineContextElement
6 | import kotlin.coroutines.CoroutineContext
7 |
8 | /**
9 | * @suppress
10 | */
11 | @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
12 | class CoreCoroutineExceptionHandler(private val handler: (PayPalSDKError) -> Unit) :
13 | AbstractCoroutineContextElement(CoroutineExceptionHandler), CoroutineExceptionHandler {
14 |
15 | override fun handleException(context: CoroutineContext, exception: Throwable) {
16 | val error = when (exception) {
17 | is PayPalSDKError -> exception
18 | else -> {
19 | val message = exception.localizedMessage ?: "Something went wrong"
20 | PayPalSDKError(0, message)
21 | }
22 | }
23 | handler(error)
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/CorePayments/src/main/java/com/paypal/android/corepayments/Environment.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android.corepayments
2 |
3 | enum class Environment(internal val url: String, internal val graphQLEndpoint: String) {
4 | LIVE(
5 | "https://api-m.paypal.com",
6 | "https://www.paypal.com"
7 | ),
8 | SANDBOX(
9 | "https://api-m.sandbox.paypal.com",
10 | "https://www.sandbox.paypal.com"
11 | ),
12 | }
13 |
--------------------------------------------------------------------------------
/CorePayments/src/main/java/com/paypal/android/corepayments/Http.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android.corepayments
2 |
3 | import android.util.Log
4 | import kotlinx.coroutines.CoroutineDispatcher
5 | import kotlinx.coroutines.Dispatchers
6 | import kotlinx.coroutines.withContext
7 | import java.io.IOException
8 | import java.lang.IllegalStateException
9 | import java.net.HttpURLConnection
10 | import java.net.UnknownHostException
11 |
12 | internal class Http(
13 | private val dispatcher: CoroutineDispatcher = Dispatchers.IO,
14 | private val httpResponseParser: HttpResponseParser = HttpResponseParser()
15 | ) {
16 |
17 | companion object {
18 | private val TAG = Http::class.qualifiedName
19 | }
20 |
21 | suspend fun send(httpRequest: HttpRequest): HttpResponse =
22 | withContext(dispatcher) {
23 | runCatching {
24 | val url = httpRequest.url
25 | val connection = url.openConnection() as HttpURLConnection
26 |
27 | connection.requestMethod = httpRequest.method.name
28 |
29 | // add headers
30 | for ((key, value) in httpRequest.headers) {
31 | connection.addRequestProperty(key, value)
32 | }
33 |
34 | if (httpRequest.method == HttpMethod.POST) {
35 | try {
36 | connection.doOutput = true
37 | connection.outputStream.write(httpRequest.body?.toByteArray())
38 | connection.outputStream.flush()
39 | connection.outputStream.close()
40 | } catch (e: IOException) {
41 | Log.d(TAG, "Error closing connection output stream:")
42 | Log.d(TAG, e.stackTrace.toString())
43 | }
44 | }
45 |
46 | connection.connect()
47 | httpResponseParser.parse(connection)
48 | }.recover {
49 | val status = when (it) {
50 | is UnknownHostException -> HttpResponse.STATUS_UNKNOWN_HOST
51 | is IllegalStateException -> HttpResponse.SERVER_ERROR
52 | else -> HttpResponse.STATUS_UNDETERMINED
53 | }
54 | HttpResponse(status = status, error = it)
55 | }.getOrNull()!!
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/CorePayments/src/main/java/com/paypal/android/corepayments/HttpMethod.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android.corepayments
2 |
3 | import androidx.annotation.RestrictTo
4 |
5 | /**
6 | * @suppress
7 | */
8 | @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
9 | enum class HttpMethod {
10 | GET, POST
11 | }
12 |
--------------------------------------------------------------------------------
/CorePayments/src/main/java/com/paypal/android/corepayments/HttpRequest.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android.corepayments
2 |
3 | import java.net.URL
4 |
5 | internal data class HttpRequest(
6 | val url: URL,
7 | val method: HttpMethod = HttpMethod.GET,
8 | val body: String? = null,
9 | val headers: MutableMap = mutableMapOf(),
10 | )
11 |
--------------------------------------------------------------------------------
/CorePayments/src/main/java/com/paypal/android/corepayments/HttpResponse.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android.corepayments
2 |
3 | import androidx.annotation.RestrictTo
4 | import java.net.HttpURLConnection
5 |
6 | /**
7 | * @suppress
8 | */
9 | @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
10 | data class HttpResponse(
11 | val status: Int,
12 | val headers: Map = emptyMap(),
13 | val body: String? = null,
14 | val error: Throwable? = null
15 | ) {
16 | companion object {
17 | const val STATUS_UNDETERMINED = -1
18 | const val STATUS_UNKNOWN_HOST = -2
19 | const val SERVER_ERROR = -3
20 |
21 | val SUCCESSFUL_STATUS_CODES = HttpURLConnection.HTTP_OK..299
22 | }
23 |
24 | var isSuccessful: Boolean = status in SUCCESSFUL_STATUS_CODES
25 | }
26 |
--------------------------------------------------------------------------------
/CorePayments/src/main/java/com/paypal/android/corepayments/LoadRawResourceResult.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android.corepayments
2 |
3 | sealed class LoadRawResourceResult {
4 | data class Success(val value: String) : LoadRawResourceResult()
5 | data class Failure(val error: PayPalSDKError) : LoadRawResourceResult()
6 | }
7 |
--------------------------------------------------------------------------------
/CorePayments/src/main/java/com/paypal/android/corepayments/OrderErrorDetail.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android.corepayments
2 |
3 | import androidx.annotation.RestrictTo
4 |
5 | /**
6 | * @suppress
7 | */
8 | @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
9 | data class OrderErrorDetail(
10 | val issue: String,
11 | val description: String
12 | ) {
13 | override fun toString(): String {
14 | return "Issue: $issue.\n" +
15 | "Error description: $description"
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/CorePayments/src/main/java/com/paypal/android/corepayments/OrderStatus.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android.corepayments
2 |
3 | import androidx.annotation.RestrictTo
4 |
5 | /**
6 | * The status of an order.
7 | * @suppress
8 | */
9 | @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
10 | enum class OrderStatus {
11 |
12 | /**
13 | * The order was created
14 | */
15 | CREATED,
16 |
17 | /**
18 | * The order was approved with a valid payment source
19 | */
20 | APPROVED,
21 |
22 | /**
23 | * The order is completed and paid
24 | */
25 | COMPLETED,
26 |
27 | /**
28 | * The payer is required to take additional action before the order can be approved
29 | */
30 | PAYER_ACTION_REQUIRED
31 | }
32 |
--------------------------------------------------------------------------------
/CorePayments/src/main/java/com/paypal/android/corepayments/PayPalSDKError.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android.corepayments
2 |
3 | import androidx.annotation.RestrictTo
4 |
5 | /**
6 | * Class used to describe PayPal errors when they occur.
7 | */
8 | class PayPalSDKError @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) constructor(
9 | val code: Int,
10 | val errorDescription: String,
11 | val correlationId: String? = null,
12 | reason: Throwable? = null
13 | ) : Exception("Error: $code - Description: $errorDescription", reason)
14 |
--------------------------------------------------------------------------------
/CorePayments/src/main/java/com/paypal/android/corepayments/PayPalSDKErrorCode.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android.corepayments
2 |
3 | import androidx.annotation.RestrictTo
4 |
5 | /**
6 | * @suppress
7 | */
8 | @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
9 | enum class PayPalSDKErrorCode {
10 | UNKNOWN,
11 | DATA_PARSING_ERROR,
12 | UNKNOWN_HOST,
13 | NO_RESPONSE_DATA,
14 | INVALID_URL_REQUEST,
15 | SERVER_RESPONSE_ERROR,
16 | CHECKOUT_ERROR,
17 | NATIVE_CHECKOUT_ERROR,
18 | GRAPHQL_JSON_INVALID_ERROR
19 | }
20 |
--------------------------------------------------------------------------------
/CorePayments/src/main/java/com/paypal/android/corepayments/ResourceLoader.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android.corepayments
2 |
3 | import android.content.Context
4 | import android.content.res.Resources
5 | import androidx.annotation.RawRes
6 | import androidx.annotation.RestrictTo
7 | import kotlinx.coroutines.Dispatchers
8 | import kotlinx.coroutines.withContext
9 | import java.io.IOException
10 |
11 | /**
12 | * Convenience class to simplify interaction with Android resource APIs.
13 | * @suppress
14 | */
15 | @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
16 | class ResourceLoader {
17 |
18 | /**
19 | * Load an Android raw resource as a String using a background IO thread.
20 | *
21 | * @param context Android context
22 | * @param resId ID of the resource that will be loaded
23 | */
24 | suspend fun loadRawResource(context: Context, @RawRes resId: Int): LoadRawResourceResult =
25 | withContext(Dispatchers.IO) {
26 | try {
27 | val resInputStream = context.resources.openRawResource(resId)
28 | val resAsBytes = ByteArray(resInputStream.available())
29 | resInputStream.read(resAsBytes)
30 | resInputStream.close()
31 | LoadRawResourceResult.Success(String(resAsBytes))
32 | } catch (e: Resources.NotFoundException) {
33 | val errorDescription = "Resource with id $resId not found."
34 | val resourceNotFoundError = PayPalSDKError(0, errorDescription, reason = e)
35 | LoadRawResourceResult.Failure(resourceNotFoundError)
36 | } catch (e: IOException) {
37 | val errorDescription = "Error loading resource with id $resId."
38 | val ioError = PayPalSDKError(0, errorDescription, reason = e)
39 | LoadRawResourceResult.Failure(ioError)
40 | }
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/CorePayments/src/main/java/com/paypal/android/corepayments/RestClient.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android.corepayments
2 |
3 | import androidx.annotation.RestrictTo
4 | import java.net.URL
5 | import java.util.Locale
6 |
7 | /**
8 | * @suppress
9 | */
10 | @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
11 | class RestClient internal constructor(
12 | private val configuration: CoreConfig,
13 | private val http: Http = Http(),
14 | private val language: String = Locale.getDefault().language
15 | ) {
16 |
17 | constructor(configuration: CoreConfig) : this(configuration, Http())
18 |
19 | suspend fun send(apiRequest: APIRequest): HttpResponse {
20 | val httpRequest = createHttpRequestFromAPIRequest(apiRequest, configuration)
21 | return http.send(httpRequest)
22 | }
23 |
24 | private fun createHttpRequestFromAPIRequest(
25 | apiRequest: APIRequest,
26 | configuration: CoreConfig,
27 | ): HttpRequest {
28 | val path = apiRequest.path
29 | val baseUrl = configuration.environment.url
30 |
31 | val url = URL("$baseUrl/$path")
32 | val method = apiRequest.method
33 | val body = apiRequest.body
34 |
35 | // default headers
36 | val headers: MutableMap = mutableMapOf(
37 | "Accept-Encoding" to "gzip",
38 | "Accept-Language" to language
39 | )
40 |
41 | // use client-id-only Basic authentication
42 | val credentials = "${configuration.clientId}:"
43 | headers["Authorization"] = "Basic ${credentials.base64encoded()}"
44 |
45 | if (method == HttpMethod.POST) {
46 | headers["Content-Type"] = "application/json"
47 | }
48 | return HttpRequest(url, method, body, headers)
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/CorePayments/src/main/java/com/paypal/android/corepayments/analytics/AnalyticsEventData.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android.corepayments.analytics
2 |
3 | // Ref: https://blog.klipse.tech/databook/2022/06/22/separate-code-from-data.html
4 | internal data class AnalyticsEventData(
5 | val environment: String,
6 | val eventName: String,
7 | val timestamp: Long,
8 | val orderId: String?,
9 | val buttonType: String? = null
10 | )
11 |
--------------------------------------------------------------------------------
/CorePayments/src/main/java/com/paypal/android/corepayments/analytics/DeviceData.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android.corepayments.analytics
2 |
3 | internal data class DeviceData(
4 | val appId: String,
5 | val appName: String,
6 | val clientOS: String,
7 | val clientSDKVersion: String,
8 | val merchantAppVersion: String?,
9 | val deviceManufacturer: String,
10 | val deviceModel: String,
11 | val isSimulator: Boolean,
12 | )
13 |
--------------------------------------------------------------------------------
/CorePayments/src/main/java/com/paypal/android/corepayments/graphql/GraphQLError.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android.corepayments.graphql
2 |
3 | import androidx.annotation.RestrictTo
4 |
5 | /**
6 | * @suppress
7 | */
8 | @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
9 | data class GraphQLError(
10 | val message: String,
11 | val extensions: List? = null
12 | )
13 |
--------------------------------------------------------------------------------
/CorePayments/src/main/java/com/paypal/android/corepayments/graphql/GraphQLExtension.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android.corepayments.graphql
2 |
3 | import androidx.annotation.RestrictTo
4 |
5 | /**
6 | * @suppress
7 | */
8 | @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
9 | data class GraphQLExtension(
10 | val correlationId: String,
11 | val code: String? = null
12 | )
13 |
--------------------------------------------------------------------------------
/CorePayments/src/main/java/com/paypal/android/corepayments/graphql/GraphQLResult.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android.corepayments.graphql
2 |
3 | import androidx.annotation.RestrictTo
4 | import com.paypal.android.corepayments.PayPalSDKError
5 | import org.json.JSONObject
6 |
7 | /**
8 | * @suppress
9 | */
10 | @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
11 | sealed class GraphQLResult {
12 |
13 | data class Success(
14 | val data: JSONObject? = null,
15 | val extensions: List? = null,
16 | val errors: List? = null,
17 | val correlationId: String? = null
18 | ) : GraphQLResult()
19 |
20 | data class Failure(val error: PayPalSDKError) : GraphQLResult()
21 | }
22 |
--------------------------------------------------------------------------------
/CorePayments/src/test/java/com/paypal/android/corepayments/CoreConfigUnitTest.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android.corepayments
2 |
3 | import org.junit.Assert.assertEquals
4 | import org.junit.Test
5 |
6 | class CoreConfigUnitTest {
7 |
8 | @Test
9 | fun `it should default to SANDBOX environment`() {
10 | val sut = CoreConfig("fake-client-id")
11 | assertEquals(Environment.SANDBOX, sut.environment)
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/CorePayments/src/test/java/com/paypal/android/corepayments/EnvironmentUnitTest.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android.corepayments
2 |
3 | import org.junit.Assert.assertEquals
4 | import org.junit.Test
5 |
6 | class EnvironmentUnitTest {
7 |
8 | @Test
9 | fun `it should return the correct url for the LIVE environment`() {
10 | assertEquals("https://api-m.paypal.com", Environment.LIVE.url)
11 | }
12 |
13 | @Test
14 | fun `it should return the correct url for the SANDBOX environment`() {
15 | assertEquals("https://api-m.sandbox.paypal.com", Environment.SANDBOX.url)
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/CorePayments/src/test/java/com/paypal/android/corepayments/TestUtils.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android.corepayments
2 |
3 | import kotlinx.coroutines.runBlocking
4 | import org.junit.Assert.assertThrows
5 |
6 | inline fun assertThrows(
7 | noinline executable: suspend () -> Unit
8 | ) = assertThrows(T::class.java) {
9 | runBlocking {
10 | executable()
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/CorePayments/src/test/resources/robolectric.properties:
--------------------------------------------------------------------------------
1 | # TODO: remove this file when Robolectric supports API level 35 (Android 15)
2 | sdk=33
3 |
--------------------------------------------------------------------------------
/Demo/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/Demo/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
--------------------------------------------------------------------------------
/Demo/src/androidTest/java/com/paypal/android/CardTest.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android
2 |
3 | import androidx.compose.ui.test.ExperimentalTestApi
4 | import androidx.compose.ui.test.hasText
5 | import androidx.compose.ui.test.junit4.createAndroidComposeRule
6 | import androidx.compose.ui.test.onNodeWithText
7 | import androidx.compose.ui.test.performClick
8 | import androidx.test.ext.junit.runners.AndroidJUnit4
9 | import org.junit.Rule
10 | import org.junit.Test
11 | import org.junit.runner.RunWith
12 |
13 | @OptIn(ExperimentalTestApi::class)
14 | @RunWith(AndroidJUnit4::class)
15 | class CardTest {
16 |
17 | @get:Rule
18 | val composeTestRule = createAndroidComposeRule()
19 |
20 | @Test
21 | fun approveOrder() {
22 | val waitTimeoutMs = 15_000L
23 |
24 | composeTestRule.onNodeWithText("Approve Order").performClick()
25 |
26 | composeTestRule.waitUntilExactlyOneExists(hasText("New Visa"), waitTimeoutMs)
27 | composeTestRule.onNodeWithText("New Visa").performClick()
28 |
29 | composeTestRule.waitUntilExactlyOneExists(hasText("Create Order"), waitTimeoutMs)
30 | composeTestRule.onNodeWithText("Create Order").performClick()
31 |
32 | // wait for approve order form
33 | composeTestRule.waitUntilExactlyOneExists(hasText("APPROVE ORDER"), waitTimeoutMs)
34 |
35 | // skip 3DS for this test
36 | composeTestRule.onNodeWithText("SCA").performClick()
37 | composeTestRule.onNodeWithText("WHEN REQUIRED").performClick()
38 |
39 | // approve the order
40 | composeTestRule.onNodeWithText("APPROVE ORDER").performClick()
41 |
42 | composeTestRule.waitUntilExactlyOneExists(hasText("Complete Order"), waitTimeoutMs)
43 | composeTestRule.onNodeWithText("AUTHORIZE ORDER").performClick()
44 |
45 | composeTestRule.waitUntilExactlyOneExists(hasText("Order Complete"), waitTimeoutMs)
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/Demo/src/androidTestShared/java/com/paypal/android/testutils/AppDriver.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android.testutils
2 |
3 | import android.content.Context
4 | import android.content.Intent
5 | import android.content.pm.PackageManager
6 | import androidx.test.core.app.ApplicationProvider
7 | import androidx.test.platform.app.InstrumentationRegistry
8 | import androidx.test.uiautomator.By
9 | import androidx.test.uiautomator.UiDevice
10 | import androidx.test.uiautomator.UiObject
11 | import androidx.test.uiautomator.UiSelector
12 | import androidx.test.uiautomator.Until
13 | import org.junit.Assert
14 |
15 | // Ref: https://github.com/android/testing-samples
16 | class AppDriver(private val appPackage: String) {
17 |
18 | companion object {
19 | const val LAUNCH_TIMEOUT = 10000L
20 | }
21 |
22 | private val device: UiDevice =
23 | UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
24 |
25 | fun launchAppFromHomeScreen() {
26 | device.pressHome()
27 |
28 | val launcherPackage = getLauncherPackageName()
29 | Assert.assertNotNull(launcherPackage)
30 | device.wait(Until.hasObject(By.pkg(launcherPackage).depth(0)), LAUNCH_TIMEOUT)
31 |
32 | val context: Context = ApplicationProvider.getApplicationContext()
33 | val intent = context.packageManager.getLaunchIntentForPackage(appPackage)!!
34 | intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)
35 |
36 | context.startActivity(intent)
37 | device.wait(Until.hasObject(By.pkg(appPackage).depth(0)), LAUNCH_TIMEOUT)
38 | }
39 |
40 | fun findResById(id: String): UiObject {
41 | return device.findObject(UiSelector().resourceId(id))
42 | }
43 |
44 | fun findText(text: String): UiObject {
45 | return device.findObject(UiSelector().text(text))
46 | }
47 |
48 | fun waitForText(text: String, timeout: Long? = null) {
49 | device.wait(Until.hasObject(By.text(text)), timeout ?: LAUNCH_TIMEOUT)
50 | }
51 |
52 | private fun getLauncherPackageName(): String {
53 | val intent = Intent(Intent.ACTION_MAIN)
54 | intent.addCategory(Intent.CATEGORY_HOME)
55 |
56 | val context: Context = ApplicationProvider.getApplicationContext()
57 | val resolveInfo = context.packageManager.resolveActivity(
58 | intent,
59 | PackageManager.MATCH_DEFAULT_ONLY
60 | )
61 | return resolveInfo!!.activityInfo.packageName
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/Demo/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
17 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/Demo/src/main/ic_launcher-playstore.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/paypal/paypal-android/9d77a9e760518e586a64c135ad7f225e4293fb20/Demo/src/main/ic_launcher-playstore.png
--------------------------------------------------------------------------------
/Demo/src/main/java/com/paypal/android/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android
2 |
3 | import android.os.Bundle
4 | import androidx.activity.ComponentActivity
5 | import androidx.activity.compose.setContent
6 | import androidx.compose.foundation.ExperimentalFoundationApi
7 | import androidx.compose.material3.ExperimentalMaterial3Api
8 | import androidx.compose.ui.ExperimentalComposeUiApi
9 | import com.paypal.android.ui.DemoApp
10 | import dagger.hilt.android.AndroidEntryPoint
11 |
12 | @AndroidEntryPoint
13 | class MainActivity : ComponentActivity() {
14 |
15 | @ExperimentalComposeUiApi
16 | @ExperimentalMaterial3Api
17 | @ExperimentalFoundationApi
18 | override fun onCreate(savedInstanceState: Bundle?) {
19 | super.onCreate(savedInstanceState)
20 | setContent {
21 | DemoApp()
22 | }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/Demo/src/main/java/com/paypal/android/MainApplication.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android
2 |
3 | import android.app.Application
4 | import dagger.hilt.android.HiltAndroidApp
5 |
6 | @HiltAndroidApp
7 | class MainApplication : Application()
8 |
--------------------------------------------------------------------------------
/Demo/src/main/java/com/paypal/android/api/model/CardPaymentToken.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android.api.model
2 |
3 | data class CardPaymentToken(
4 | val id: String,
5 | val customerId: String,
6 | val cardLast4: String,
7 | val cardBrand: String
8 | )
9 |
--------------------------------------------------------------------------------
/Demo/src/main/java/com/paypal/android/api/model/CardSetupToken.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android.api.model
2 |
3 | data class CardSetupToken(
4 | val id: String,
5 | val customerId: String,
6 | val status: String,
7 | val verificationStatus: String? = null,
8 | )
9 |
--------------------------------------------------------------------------------
/Demo/src/main/java/com/paypal/android/api/model/ClientId.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android.api.model
2 |
3 | data class ClientId(
4 | val value: String
5 | )
6 |
--------------------------------------------------------------------------------
/Demo/src/main/java/com/paypal/android/api/model/Order.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android.api.model
2 |
3 | import android.os.Parcelable
4 | import kotlinx.parcelize.Parcelize
5 |
6 | @Parcelize
7 | data class Order(
8 | val id: String? = null,
9 | val intent: String? = null,
10 | val status: String? = null,
11 | val cardLast4: String? = null,
12 | val cardBrand: String? = null,
13 | val vaultId: String? = null,
14 | val customerId: String? = null
15 | ) : Parcelable
16 |
--------------------------------------------------------------------------------
/Demo/src/main/java/com/paypal/android/api/model/OrderIntent.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android.api.model
2 |
3 | /**
4 | * The intent to either capture payment immediately or authorize a payment for an order after order creation
5 | */
6 | enum class OrderIntent {
7 | /**
8 | * The merchant intends to capture payment immediately after the customer makes a payment.
9 | */
10 | CAPTURE,
11 | /**
12 | * The merchant intends to authorize a payment and place funds on hold after the customer makes
13 | * a payment. Authorized payments are best captured within three days of authorization but are
14 | * available to capture for up to 29 days. After the three-day honor period, the original authorized
15 | * payment expires and you must re-authorize the payment. You must make a separate request to
16 | * capture payments on demand. This intent is not supported when you have more than one
17 | * `purchase_unit` within your order.
18 | */
19 | AUTHORIZE
20 | }
21 |
--------------------------------------------------------------------------------
/Demo/src/main/java/com/paypal/android/api/model/PayPalPaymentToken.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android.api.model
2 |
3 | data class PayPalPaymentToken(
4 | val id: String,
5 | val customerId: String,
6 | )
7 |
--------------------------------------------------------------------------------
/Demo/src/main/java/com/paypal/android/api/model/PayPalSetupToken.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android.api.model
2 |
3 | data class PayPalSetupToken(
4 | val id: String,
5 | val customerId: String,
6 | val status: String
7 | )
8 |
--------------------------------------------------------------------------------
/Demo/src/main/java/com/paypal/android/api/services/MerchantIntegration.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android.api.services
2 |
3 | enum class MerchantIntegration(val baseUrl: String) {
4 | DEFAULT("https://sdk-sample-merchant-server.herokuapp.com/"),
5 | DIRECT("https://sdk-sample-merchant-server.herokuapp.com/direct/"),
6 | CONNECTED_PATH("https://sdk-sample-merchant-server.herokuapp.com/connected_path/"),
7 | MANAGED_PATH("https://sdk-sample-merchant-server.herokuapp.com/managed_path/"),
8 | }
9 |
--------------------------------------------------------------------------------
/Demo/src/main/java/com/paypal/android/api/services/SDKSampleServerException.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android.api.services
2 |
3 | class SDKSampleServerException(message: String?, cause: Throwable?) : Exception(message, cause)
4 |
--------------------------------------------------------------------------------
/Demo/src/main/java/com/paypal/android/api/services/SDKSampleServerResult.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android.api.services
2 |
3 | import com.paypal.android.uishared.state.ActionState
4 |
5 | sealed class SDKSampleServerResult {
6 | data class Success(val value: S) : SDKSampleServerResult()
7 | data class Failure(val value: E) : SDKSampleServerResult()
8 |
9 | fun mapToActionState(): ActionState = when (this) {
10 | is Success -> ActionState.Success(value)
11 | is Failure -> ActionState.Failure(value)
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/Demo/src/main/java/com/paypal/android/di/NetworkModule.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android.di
2 |
3 | import com.paypal.android.api.services.SDKSampleServerAPI
4 | import dagger.Module
5 | import dagger.Provides
6 | import dagger.hilt.InstallIn
7 | import dagger.hilt.components.SingletonComponent
8 |
9 | @Module
10 | @InstallIn(SingletonComponent::class)
11 | object NetworkModule {
12 |
13 | @Provides
14 | fun provideSDKSampleServerAPI(): SDKSampleServerAPI = SDKSampleServerAPI()
15 | }
16 |
--------------------------------------------------------------------------------
/Demo/src/main/java/com/paypal/android/models/OrderRequest.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android.models
2 |
3 | import com.paypal.android.api.model.OrderIntent
4 |
5 | data class OrderRequest(
6 | val intent: OrderIntent,
7 | val shouldVault: Boolean = false
8 | )
9 |
--------------------------------------------------------------------------------
/Demo/src/main/java/com/paypal/android/ui/DemoAppDestinations.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android.ui
2 |
3 | object DemoAppDestinations {
4 | const val CARD_APPROVE_ORDER = "card_approve_order"
5 | const val FEATURES_ROUTE = "features"
6 | const val CARD_VAULT = "card_vault"
7 | const val PAYPAL_WEB = "paypal_web"
8 | const val PAYPAL_WEB_VAULT = "paypal_web_vault"
9 | const val PAYPAL_BUTTONS = "paypal_buttons"
10 | const val PAYPAL_STATIC_BUTTONS = "paypal_static_buttons"
11 | const val SELECT_TEST_CARD = "select_test_card"
12 |
13 | fun titleForDestination(destination: String?): String = when (destination) {
14 | CARD_APPROVE_ORDER -> "Card Approve Order"
15 | FEATURES_ROUTE -> "Features"
16 | CARD_VAULT -> "Vault Card"
17 | PAYPAL_WEB -> "PayPal Web"
18 | PAYPAL_BUTTONS -> "PayPal Buttons"
19 | PAYPAL_STATIC_BUTTONS -> "PayPal Static Buttons"
20 | SELECT_TEST_CARD -> "Select a Test Card"
21 | PAYPAL_WEB_VAULT -> "PayPal Web Vault"
22 | else -> "Demo"
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/Demo/src/main/java/com/paypal/android/ui/approveorder/ApproveOrderUiState.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android.ui.approveorder
2 |
3 | import androidx.compose.runtime.Immutable
4 | import com.paypal.android.api.model.Order
5 | import com.paypal.android.api.model.OrderIntent
6 | import com.paypal.android.cardpayments.threedsecure.SCA
7 | import com.paypal.android.uishared.enums.StoreInVaultOption
8 | import com.paypal.android.uishared.state.ActionState
9 |
10 | @Immutable
11 | data class ApproveOrderUiState(
12 | val createOrderState: ActionState = ActionState.Idle,
13 | val approveOrderState: ActionState = ActionState.Idle,
14 | val completeOrderState: ActionState = ActionState.Idle,
15 | val scaOption: SCA = SCA.SCA_ALWAYS,
16 | val cardNumber: String = "",
17 | val cardExpirationDate: String = "",
18 | val cardSecurityCode: String = "",
19 | val intentOption: OrderIntent = OrderIntent.AUTHORIZE,
20 | val shouldVault: StoreInVaultOption = StoreInVaultOption.NO,
21 | ) {
22 | val isCreateOrderSuccessful: Boolean
23 | get() = createOrderState is ActionState.Success
24 |
25 | val isApproveOrderSuccessful: Boolean
26 | get() = approveOrderState is ActionState.Success
27 | }
28 |
--------------------------------------------------------------------------------
/Demo/src/main/java/com/paypal/android/ui/approveorder/CardFormatter.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android.ui.approveorder
2 |
3 | object CardFormatter {
4 |
5 | /**
6 | * Formats a card number string.
7 | * Example: "4000000000001091" -> "4000 0000 0000 1091"
8 | *
9 | * @param newCardNumber - card number to format
10 | * @param previousCardNumber - previous value that was entered into the card field. This value
11 | * is needed to handle the deletion of characters.
12 | */
13 | fun formatCardNumber(newCardNumber: String, previousCardNumber: String = ""): String {
14 | if (newCardNumber.length < previousCardNumber.length) return newCardNumber
15 | var cardString = newCardNumber.replace(" ", "")
16 |
17 | val cardType = getCardType(cardString)
18 | return if (previousCardNumber.length == cardType.maxCardLength + cardType.cardNumberIndices.size) {
19 | previousCardNumber
20 | } else {
21 | for (index in cardType.cardNumberIndices) {
22 | if (index <= cardString.length) {
23 | cardString = cardString.insertChar(index, ' ')
24 | }
25 | }
26 | cardString
27 | }
28 | }
29 |
30 | private fun String.insertChar(index: Int, char: Char): String {
31 | return substring(0, index) + char + substring(index, length)
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/Demo/src/main/java/com/paypal/android/ui/approveorder/CardType.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android.ui.approveorder
2 |
3 | import java.lang.Integer.max
4 |
5 | @Suppress("MagicNumber")
6 | enum class CardType(
7 | val cardNumberIndices: List,
8 | val digitGroupings: List,
9 | val maxCardLength: Int,
10 | val allDigitGroupingsExceptLast: List = digitGroupings.slice(
11 | 0 until max(
12 | 0,
13 | digitGroupings.size - 1
14 | )
15 | )
16 | ) {
17 | // Ref: https://stackoverflow.com/a/65726499
18 |
19 | AMERICAN_EXPRESS(
20 | cardNumberIndices = listOf(4, 11),
21 | digitGroupings = listOf(4, 6, 5),
22 | maxCardLength = 15
23 | ),
24 | VISA(
25 | cardNumberIndices = listOf(4, 9, 14),
26 | digitGroupings = listOf(4, 4, 4, 4),
27 | maxCardLength = 16
28 | ),
29 | UNKNOWN_CARD(
30 | cardNumberIndices = listOf(4, 9, 14),
31 | digitGroupings = emptyList(),
32 | maxCardLength = 16
33 | )
34 | }
35 |
36 | fun getCardType(cardNumber: String): CardType {
37 | return when {
38 | cardNumber.startsWith("34") || cardNumber.startsWith("37") -> {
39 | CardType.AMERICAN_EXPRESS
40 | }
41 |
42 | cardNumber.startsWith("4") -> CardType.VISA
43 | else -> CardType.UNKNOWN_CARD
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/Demo/src/main/java/com/paypal/android/ui/approveorder/DateVisualTransformation.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android.ui.approveorder
2 |
3 | import androidx.compose.ui.text.AnnotatedString
4 | import androidx.compose.ui.text.input.OffsetMapping
5 | import androidx.compose.ui.text.input.TransformedText
6 | import androidx.compose.ui.text.input.VisualTransformation
7 |
8 | class DateVisualTransformation : VisualTransformation {
9 |
10 | override fun filter(text: AnnotatedString): TransformedText {
11 | val dateString = DateString(text.text)
12 | return TransformedText(
13 | AnnotatedString(formatDate(dateString)),
14 | offsetMapping = DateOffsetMapping(dateString)
15 | )
16 | }
17 |
18 | private fun formatDate(dateString: DateString): String {
19 | return dateString.formatted
20 | }
21 |
22 | class DateOffsetMapping(private val dateString: DateString) : OffsetMapping {
23 |
24 | override fun originalToTransformed(offset: Int): Int {
25 | return dateString.mapRawOffsetToFormatted(offset)
26 | }
27 |
28 | override fun transformedToOriginal(offset: Int): Int {
29 | return dateString.mapFormattedOffsetToRawOffset(offset)
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/Demo/src/main/java/com/paypal/android/ui/approveorder/OrderInfo.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android.ui.approveorder
2 |
3 | data class OrderInfo(
4 | val orderId: String,
5 | val status: String?,
6 | val didAttemptThreeDSecureAuthentication: Boolean
7 | )
8 |
--------------------------------------------------------------------------------
/Demo/src/main/java/com/paypal/android/ui/approveorder/SetupTokenInfo.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android.ui.approveorder
2 |
3 | data class SetupTokenInfo(
4 | val setupTokenId: String,
5 | val status: String? = null,
6 | val didAttemptThreeDSecureAuthentication: Boolean = false
7 | )
8 |
--------------------------------------------------------------------------------
/Demo/src/main/java/com/paypal/android/ui/features/Feature.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android.ui.features
2 |
3 | import androidx.annotation.StringRes
4 | import com.paypal.android.R
5 | import com.paypal.android.ui.DemoAppDestinations
6 |
7 | enum class Feature(@StringRes val stringRes: Int, val routeName: String) {
8 | CARD_APPROVE_ORDER(R.string.feature_approve_order, DemoAppDestinations.CARD_APPROVE_ORDER),
9 | CARD_VAULT(R.string.feature_vault, DemoAppDestinations.CARD_VAULT),
10 | PAYPAL_WEB(R.string.feature_paypal_web, DemoAppDestinations.PAYPAL_WEB),
11 | PAYPAL_WEB_VAULT(R.string.feature_paypal_web_vault, DemoAppDestinations.PAYPAL_WEB_VAULT),
12 | PAYPAL_BUTTONS(R.string.feature_paypal_buttons, DemoAppDestinations.PAYPAL_BUTTONS),
13 | PAYPAL_STATIC_BUTTONS(
14 | R.string.feature_paypal_static_buttons,
15 | DemoAppDestinations.PAYPAL_STATIC_BUTTONS
16 | ),
17 | }
18 |
--------------------------------------------------------------------------------
/Demo/src/main/java/com/paypal/android/ui/paypalbuttons/ButtonFundingType.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android.ui.paypalbuttons
2 |
3 | enum class ButtonFundingType {
4 | PAYPAL,
5 | PAY_LATER,
6 | PAYPAL_CREDIT;
7 | }
8 |
--------------------------------------------------------------------------------
/Demo/src/main/java/com/paypal/android/ui/paypalbuttons/PayPalButtonColorOptionList.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android.ui.paypalbuttons
2 |
3 | import androidx.compose.runtime.Composable
4 | import androidx.compose.ui.res.stringResource
5 | import com.paypal.android.R
6 | import com.paypal.android.paymentbuttons.PayPalButtonColor
7 | import com.paypal.android.uishared.components.OptionList
8 |
9 | @Composable
10 | fun PayPalButtonColorOptionList(
11 | selectedOption: PayPalButtonColor,
12 | onSelection: (PayPalButtonColor) -> Unit
13 | ) {
14 | OptionList(
15 | title = stringResource(id = R.string.pay_pal_button_color),
16 | options = PayPalButtonColor.values().map { it.name },
17 | selectedOption = selectedOption.name,
18 | onSelectedOptionChange = { option ->
19 | onSelection(PayPalButtonColor.valueOf(option))
20 | }
21 | )
22 | }
23 |
--------------------------------------------------------------------------------
/Demo/src/main/java/com/paypal/android/ui/paypalbuttons/PayPalButtonFundingTypeOptionList.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android.ui.paypalbuttons
2 |
3 | import androidx.compose.runtime.Composable
4 | import androidx.compose.ui.res.stringResource
5 | import com.paypal.android.R
6 | import com.paypal.android.uishared.components.OptionList
7 |
8 | @Composable
9 | fun PayPalButtonFundingTypeOptionList(
10 | selectedOption: ButtonFundingType,
11 | onSelection: (ButtonFundingType) -> Unit
12 | ) {
13 | OptionList(
14 | title = stringResource(id = R.string.pay_pal_button_type),
15 | options = ButtonFundingType.values().map { it.name },
16 | selectedOption = selectedOption.name,
17 | onSelectedOptionChange = { option ->
18 | onSelection(ButtonFundingType.valueOf(option))
19 | }
20 | )
21 | }
22 |
--------------------------------------------------------------------------------
/Demo/src/main/java/com/paypal/android/ui/paypalbuttons/PayPalButtonLabelOptionList.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android.ui.paypalbuttons
2 |
3 | import androidx.compose.runtime.Composable
4 | import androidx.compose.ui.res.stringResource
5 | import com.paypal.android.R
6 | import com.paypal.android.paymentbuttons.PayPalButtonLabel
7 | import com.paypal.android.uishared.components.OptionList
8 |
9 | @Composable
10 | fun PayPalButtonLabelOptionList(
11 | selectedOption: PayPalButtonLabel,
12 | onSelection: (PayPalButtonLabel) -> Unit
13 | ) {
14 | OptionList(
15 | title = stringResource(id = R.string.pay_pal_button_label),
16 | options = PayPalButtonLabel.values().map { it.name },
17 | selectedOption = selectedOption.name,
18 | onSelectedOptionChange = { option ->
19 | onSelection(PayPalButtonLabel.valueOf(option))
20 | }
21 | )
22 | }
23 |
--------------------------------------------------------------------------------
/Demo/src/main/java/com/paypal/android/ui/paypalbuttons/PayPalButtonsUiState.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android.ui.paypalbuttons
2 |
3 | import com.paypal.android.paymentbuttons.PayPalButtonColor
4 | import com.paypal.android.paymentbuttons.PayPalButtonLabel
5 | import com.paypal.android.paymentbuttons.PayPalCreditButtonColor
6 | import com.paypal.android.paymentbuttons.PaymentButtonShape
7 | import com.paypal.android.paymentbuttons.PaymentButtonSize
8 |
9 | data class PayPalButtonsUiState(
10 | val fundingType: ButtonFundingType = ButtonFundingType.PAYPAL,
11 | val payPalCreditButtonColor: PayPalCreditButtonColor = PayPalCreditButtonColor.DARK_BLUE,
12 | val payPalButtonColor: PayPalButtonColor = PayPalButtonColor.GOLD,
13 | val payPalButtonLabel: PayPalButtonLabel = PayPalButtonLabel.PAYPAL,
14 | val paymentButtonShape: PaymentButtonShape = PaymentButtonShape.ROUNDED,
15 | val paymentButtonSize: PaymentButtonSize = PaymentButtonSize.SMALL,
16 | val customCornerRadius: Int? = null
17 | )
18 |
--------------------------------------------------------------------------------
/Demo/src/main/java/com/paypal/android/ui/paypalbuttons/PayPalButtonsViewModel.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android.ui.paypalbuttons
2 |
3 | import androidx.lifecycle.ViewModel
4 | import com.paypal.android.paymentbuttons.PayPalButtonColor
5 | import com.paypal.android.paymentbuttons.PayPalButtonLabel
6 | import com.paypal.android.paymentbuttons.PayPalCreditButtonColor
7 | import com.paypal.android.paymentbuttons.PaymentButtonShape
8 | import com.paypal.android.paymentbuttons.PaymentButtonSize
9 | import kotlinx.coroutines.flow.MutableStateFlow
10 | import kotlinx.coroutines.flow.asStateFlow
11 | import kotlinx.coroutines.flow.update
12 |
13 | class PayPalButtonsViewModel : ViewModel() {
14 | private val _uiState = MutableStateFlow(PayPalButtonsUiState())
15 | val uiState = _uiState.asStateFlow()
16 |
17 | var selectedFundingType: ButtonFundingType
18 | get() = _uiState.value.fundingType
19 | set(value) {
20 | _uiState.update { it.copy(fundingType = value) }
21 | }
22 |
23 | var payPalButtonColor: PayPalButtonColor
24 | get() = _uiState.value.payPalButtonColor
25 | set(value) {
26 | _uiState.update { it.copy(payPalButtonColor = value) }
27 | }
28 |
29 | var payPalCreditButtonColor: PayPalCreditButtonColor
30 | get() = _uiState.value.payPalCreditButtonColor
31 | set(value) {
32 | _uiState.update { it.copy(payPalCreditButtonColor = value) }
33 | }
34 |
35 | var payPalButtonLabel: PayPalButtonLabel
36 | get() = _uiState.value.payPalButtonLabel
37 | set(value) {
38 | _uiState.update { it.copy(payPalButtonLabel = value) }
39 | }
40 |
41 | var customCornerRadius: Int?
42 | get() = _uiState.value.customCornerRadius
43 | set(value) {
44 | _uiState.update { it.copy(customCornerRadius = value) }
45 | }
46 | var paymentButtonShape: PaymentButtonShape
47 | get() = _uiState.value.paymentButtonShape
48 | set(value) {
49 | _uiState.update {
50 | it.copy(
51 | paymentButtonShape = value,
52 | customCornerRadius = null
53 | )
54 | }
55 | }
56 |
57 | var paymentButtonSize: PaymentButtonSize
58 | get() = _uiState.value.paymentButtonSize
59 | set(value) {
60 | _uiState.update { it.copy(paymentButtonSize = value) }
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/Demo/src/main/java/com/paypal/android/ui/paypalbuttons/PayPalCreditButtonColorOptionList.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android.ui.paypalbuttons
2 |
3 | import androidx.compose.runtime.Composable
4 | import androidx.compose.ui.res.stringResource
5 | import com.paypal.android.R
6 | import com.paypal.android.paymentbuttons.PayPalCreditButtonColor
7 | import com.paypal.android.uishared.components.OptionList
8 |
9 | @Composable
10 | fun PayPalCreditButtonColorOptionList(
11 | selectedOption: PayPalCreditButtonColor,
12 | onSelection: (PayPalCreditButtonColor) -> Unit
13 | ) {
14 | OptionList(
15 | title = stringResource(id = R.string.pay_pal_button_color),
16 | options = PayPalCreditButtonColor.values().map { it.name },
17 | selectedOption = selectedOption.name,
18 | onSelectedOptionChange = { option ->
19 | onSelection(PayPalCreditButtonColor.valueOf(option))
20 | }
21 | )
22 | }
23 |
--------------------------------------------------------------------------------
/Demo/src/main/java/com/paypal/android/ui/paypalbuttons/PaymentButtonShapeOptionList.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android.ui.paypalbuttons
2 |
3 | import androidx.compose.runtime.Composable
4 | import androidx.compose.ui.res.stringResource
5 | import com.paypal.android.R
6 | import com.paypal.android.paymentbuttons.PaymentButtonShape
7 | import com.paypal.android.uishared.components.OptionList
8 |
9 | @Composable
10 | fun PaymentButtonShapeOptionList(
11 | selectedOption: PaymentButtonShape,
12 | onSelection: (PaymentButtonShape) -> Unit
13 | ) {
14 | OptionList(
15 | title = stringResource(id = R.string.pay_pal_button_shape),
16 | options = PaymentButtonShape.values().map { it.name },
17 | selectedOption = selectedOption.name,
18 | onSelectedOptionChange = { option ->
19 | onSelection(PaymentButtonShape.valueOf(option))
20 | }
21 | )
22 | }
23 |
--------------------------------------------------------------------------------
/Demo/src/main/java/com/paypal/android/ui/paypalbuttons/PaymentButtonSizeOptionList.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android.ui.paypalbuttons
2 |
3 | import androidx.compose.runtime.Composable
4 | import androidx.compose.ui.res.stringResource
5 | import com.paypal.android.R
6 | import com.paypal.android.paymentbuttons.PaymentButtonSize
7 | import com.paypal.android.uishared.components.OptionList
8 |
9 | @Composable
10 | fun PaymentButtonSizeOptionList(
11 | selectedOption: PaymentButtonSize,
12 | onSelection: (PaymentButtonSize) -> Unit
13 | ) {
14 | OptionList(
15 | title = stringResource(id = R.string.pay_pal_button_size),
16 | options = PaymentButtonSize.values().map { it.name },
17 | selectedOption = selectedOption.name,
18 | onSelectedOptionChange = { option ->
19 | onSelection(PaymentButtonSize.valueOf(option))
20 | }
21 | )
22 | }
23 |
--------------------------------------------------------------------------------
/Demo/src/main/java/com/paypal/android/ui/paypalstaticbuttons/PayPalStaticButtonsView.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android.ui.paypalstaticbuttons
2 |
3 | import android.annotation.SuppressLint
4 | import android.view.LayoutInflater
5 | import androidx.compose.foundation.layout.fillMaxSize
6 | import androidx.compose.runtime.Composable
7 | import androidx.compose.ui.Modifier
8 | import androidx.compose.ui.viewinterop.AndroidView
9 | import com.paypal.android.R
10 |
11 | @SuppressLint("InflateParams")
12 | @Composable
13 | fun PayPalStaticButtonsView() {
14 | AndroidView(
15 | factory = { context ->
16 | val view = LayoutInflater.from(context)
17 | .inflate(R.layout.pay_later_button_test_layout, null, false)
18 |
19 | view
20 | },
21 | modifier = Modifier.fillMaxSize()
22 | )
23 | }
24 |
--------------------------------------------------------------------------------
/Demo/src/main/java/com/paypal/android/ui/paypalweb/PayPalWebCheckoutResultView.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android.ui.paypalweb
2 |
3 | import androidx.compose.foundation.layout.Column
4 | import androidx.compose.foundation.layout.padding
5 | import androidx.compose.runtime.Composable
6 | import androidx.compose.ui.Modifier
7 | import com.paypal.android.uishared.components.PropertyView
8 | import com.paypal.android.utils.UIConstants
9 |
10 | @Composable
11 | fun PayPalWebCheckoutResultView(orderId: String?, payerId: String?) {
12 | Column(
13 | verticalArrangement = UIConstants.spacingMedium,
14 | modifier = Modifier.padding(UIConstants.paddingMedium)
15 | ) {
16 | PropertyView(name = "Order ID", value = orderId)
17 | PropertyView(name = "Payer ID", value = payerId)
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/Demo/src/main/java/com/paypal/android/ui/paypalweb/PayPalWebUiState.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android.ui.paypalweb
2 |
3 | import com.paypal.android.api.model.Order
4 | import com.paypal.android.api.model.OrderIntent
5 | import com.paypal.android.paypalwebpayments.PayPalWebCheckoutFinishStartResult
6 | import com.paypal.android.paypalwebpayments.PayPalWebCheckoutFundingSource
7 | import com.paypal.android.uishared.state.ActionState
8 |
9 | data class PayPalWebUiState(
10 | val intentOption: OrderIntent = OrderIntent.AUTHORIZE,
11 | val createOrderState: ActionState = ActionState.Idle,
12 | val payPalWebCheckoutState: ActionState = ActionState.Idle,
13 | val completeOrderState: ActionState = ActionState.Idle,
14 | val fundingSource: PayPalWebCheckoutFundingSource = PayPalWebCheckoutFundingSource.PAYPAL
15 | ) {
16 | val isCreateOrderSuccessful: Boolean
17 | get() = createOrderState is ActionState.Success
18 |
19 | val isPayPalWebCheckoutSuccessful: Boolean
20 | get() = payPalWebCheckoutState is ActionState.Success
21 | }
22 |
--------------------------------------------------------------------------------
/Demo/src/main/java/com/paypal/android/ui/paypalweb/StartPayPalWebCheckoutForm.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android.ui.paypalweb
2 |
3 | import androidx.compose.foundation.layout.Column
4 | import androidx.compose.runtime.Composable
5 | import androidx.compose.ui.res.stringResource
6 | import com.paypal.android.R
7 | import com.paypal.android.paypalwebpayments.PayPalWebCheckoutFundingSource
8 | import com.paypal.android.uishared.components.EnumOptionList
9 | import com.paypal.android.utils.UIConstants
10 |
11 | @Composable
12 | fun StartPayPalWebCheckoutForm(
13 | fundingSource: PayPalWebCheckoutFundingSource,
14 | onFundingSourceChange: (PayPalWebCheckoutFundingSource) -> Unit,
15 | ) {
16 | Column(
17 | verticalArrangement = UIConstants.spacingMedium
18 | ) {
19 | EnumOptionList(
20 | title = stringResource(id = R.string.pay_pal_funding_source_title),
21 | stringArrayResId = R.array.pay_pal_funding_source_options,
22 | onSelectedOptionChange = onFundingSourceChange,
23 | selectedOption = fundingSource
24 | )
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/Demo/src/main/java/com/paypal/android/ui/paypalwebvault/PayPalWebVaultUiState.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android.ui.paypalwebvault
2 |
3 | import com.paypal.android.api.model.PayPalPaymentToken
4 | import com.paypal.android.api.model.PayPalSetupToken
5 | import com.paypal.android.paypalwebpayments.PayPalWebCheckoutFinishVaultResult
6 | import com.paypal.android.uishared.state.ActionState
7 |
8 | data class PayPalWebVaultUiState(
9 | val createSetupTokenState: ActionState = ActionState.Idle,
10 | val vaultPayPalState: ActionState = ActionState.Idle,
11 | val createPaymentTokenState: ActionState = ActionState.Idle,
12 | ) {
13 | val isCreateSetupTokenSuccessful: Boolean
14 | get() = createSetupTokenState is ActionState.Success
15 |
16 | val isVaultPayPalSuccessful: Boolean
17 | get() = vaultPayPalState is ActionState.Success
18 | }
19 |
--------------------------------------------------------------------------------
/Demo/src/main/java/com/paypal/android/ui/selectcard/SelectCardViewModel.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android.ui.selectcard
2 |
3 | import androidx.lifecycle.ViewModel
4 | import com.paypal.android.models.TestCard
5 | import java.util.Calendar
6 |
7 | class SelectCardViewModel : ViewModel() {
8 | companion object {
9 | // 2 years into the future of the current year
10 | val validExpirationYear = "${Calendar.getInstance().get(Calendar.YEAR) + 2}"
11 | }
12 |
13 | val verifiedTestCards = listOf(
14 | TestCard.VISA_VAULT_WITH_PURCHASE_NO_3DS
15 | )
16 |
17 | val nonThreeDSCards = listOf(
18 | TestCard.VISA_NO_3DS_1,
19 | TestCard.VISA_NO_3DS_2
20 | )
21 |
22 | val threeDSCards = listOf(
23 | TestCard.VISA_3DS_SUCCESSFUL_AUTH,
24 | TestCard.VISA_3DS_FAILED_SIGNATURE,
25 | TestCard.VISA_3DS_FAILED_AUTHENTICATION,
26 | TestCard.VISA_3DS_PASSIVE_AUTHENTICATION,
27 | TestCard.VISA_3DS_TRANSACTION_TIMEOUT,
28 | TestCard.VISA_3DS_NOT_ENROLLED,
29 | TestCard.VISA_3DS_AUTH_SYSTEM_UNAVAILABLE,
30 | TestCard.VISA_3DS_AUTH_ERROR,
31 | TestCard.VISA_3DS_AUTH_UNAVAILABLE,
32 | TestCard.VISA_3DS_MERCHANT_BYPASSED_AUTH,
33 | TestCard.VISA_3DS_MERCHANT_INACTIVE,
34 | TestCard.VISA_3DS_CMPI_LOOKUP_ERROR
35 | )
36 | }
37 |
--------------------------------------------------------------------------------
/Demo/src/main/java/com/paypal/android/ui/vaultcard/VaultCardUiState.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android.ui.vaultcard
2 |
3 | import com.paypal.android.api.model.CardPaymentToken
4 | import com.paypal.android.api.model.CardSetupToken
5 | import com.paypal.android.ui.approveorder.SetupTokenInfo
6 | import com.paypal.android.cardpayments.threedsecure.SCA
7 | import com.paypal.android.uishared.state.ActionState
8 |
9 | data class VaultCardUiState(
10 | val createSetupTokenState: ActionState = ActionState.Idle,
11 | val updateSetupTokenState: ActionState = ActionState.Idle,
12 | val createPaymentTokenState: ActionState = ActionState.Idle,
13 | val cardNumber: String = "",
14 | val cardExpirationDate: String = "",
15 | val cardSecurityCode: String = "",
16 | val scaOption: SCA = SCA.SCA_WHEN_REQUIRED,
17 | ) {
18 | val isCreateSetupTokenSuccessful: Boolean
19 | get() = createSetupTokenState is ActionState.Success
20 |
21 | val isVaultCardSuccessful: Boolean
22 | get() = updateSetupTokenState is ActionState.Success
23 | }
24 |
--------------------------------------------------------------------------------
/Demo/src/main/java/com/paypal/android/uishared/components/BooleanOptionList.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android.uishared.components
2 |
3 | import androidx.compose.foundation.layout.padding
4 | import androidx.compose.material3.MaterialTheme
5 | import androidx.compose.material3.Surface
6 | import androidx.compose.runtime.Composable
7 | import androidx.compose.ui.Modifier
8 | import androidx.compose.ui.tooling.preview.Preview
9 | import com.paypal.android.utils.UIConstants
10 |
11 | @Composable
12 | fun BooleanOptionList(
13 | title: String,
14 | selectedOption: Boolean,
15 | onSelectedOptionChange: (Boolean) -> Unit,
16 | modifier: Modifier = Modifier,
17 | ) {
18 | val options = listOf("NO", "YES")
19 | val selectedOptionString = if (selectedOption) "YES" else "NO"
20 | OptionList(
21 | title = title,
22 | options = options,
23 | selectedOption = selectedOptionString,
24 | onSelectedOptionChange = { value -> onSelectedOptionChange(value == "YES") },
25 | modifier = modifier
26 | )
27 | }
28 |
29 | @Preview
30 | @Composable
31 | fun BooleanOptionListPreview() {
32 | MaterialTheme {
33 | Surface {
34 | BooleanOptionList(
35 | title = "Fake Title",
36 | onSelectedOptionChange = {},
37 | selectedOption = true,
38 | modifier = Modifier.padding(UIConstants.paddingSmall)
39 | )
40 | }
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/Demo/src/main/java/com/paypal/android/uishared/components/CardPaymentTokenView.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android.uishared.components
2 |
3 | import androidx.compose.foundation.layout.Column
4 | import androidx.compose.foundation.layout.padding
5 | import androidx.compose.runtime.Composable
6 | import androidx.compose.ui.Modifier
7 | import com.paypal.android.api.model.CardPaymentToken
8 | import com.paypal.android.utils.UIConstants
9 |
10 | @Composable
11 | fun CardPaymentTokenView(paymentToken: CardPaymentToken) {
12 | Column(
13 | verticalArrangement = UIConstants.spacingMedium,
14 | modifier = Modifier.padding(UIConstants.paddingMedium)
15 | ) {
16 | PropertyView(name = "ID", value = paymentToken.id)
17 | PropertyView(name = "Customer ID", value = paymentToken.customerId)
18 | PropertyView(name = "Card Brand", value = paymentToken.cardBrand)
19 | PropertyView(name = "Card Last 4", value = paymentToken.cardLast4)
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/Demo/src/main/java/com/paypal/android/uishared/components/CardResultView.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android.uishared.components
2 |
3 | import androidx.compose.foundation.layout.Column
4 | import androidx.compose.foundation.layout.fillMaxWidth
5 | import androidx.compose.foundation.layout.padding
6 | import androidx.compose.material3.MaterialTheme
7 | import androidx.compose.material3.Surface
8 | import androidx.compose.runtime.Composable
9 | import androidx.compose.ui.Modifier
10 | import androidx.compose.ui.tooling.preview.Preview
11 | import com.paypal.android.ui.approveorder.OrderInfo
12 | import com.paypal.android.utils.UIConstants
13 |
14 | @Composable
15 | fun CardResultView(result: OrderInfo) {
16 | Column(
17 | verticalArrangement = UIConstants.spacingMedium,
18 | modifier = Modifier
19 | .fillMaxWidth()
20 | .padding(UIConstants.paddingMedium)
21 | ) {
22 | PropertyView(name = "Order ID", value = result.orderId)
23 | PropertyView(name = "Order Status", value = result.status)
24 | val didAttemptText = if (result.didAttemptThreeDSecureAuthentication) "YES" else "NO"
25 | PropertyView(name = "Did Attempt 3DS Authentication", value = didAttemptText)
26 | }
27 | }
28 |
29 | @Preview
30 | @Composable
31 | fun CardResultViewWith3DSAuth() {
32 | MaterialTheme {
33 | Surface(modifier = Modifier.fillMaxWidth()) {
34 | val result = OrderInfo(
35 | orderId = "fake-order-id",
36 | status = "fake-status",
37 | didAttemptThreeDSecureAuthentication = true
38 | )
39 | CardResultView(result)
40 | }
41 | }
42 | }
43 |
44 | @Preview
45 | @Composable
46 | fun CardResultViewWithout3DSAuth() {
47 | MaterialTheme {
48 | Surface(modifier = Modifier.fillMaxWidth()) {
49 | val result = OrderInfo(
50 | orderId = "fake-order-id",
51 | status = "fake-status",
52 | didAttemptThreeDSecureAuthentication = false
53 | )
54 | CardResultView(result)
55 | }
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/Demo/src/main/java/com/paypal/android/uishared/components/CardSetupTokenView.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android.uishared.components
2 |
3 | import androidx.compose.foundation.layout.Column
4 | import androidx.compose.foundation.layout.padding
5 | import androidx.compose.runtime.Composable
6 | import androidx.compose.ui.Modifier
7 | import com.paypal.android.api.model.CardSetupToken
8 | import com.paypal.android.utils.UIConstants
9 |
10 | @Composable
11 | fun CardSetupTokenView(setupToken: CardSetupToken) {
12 | Column(
13 | verticalArrangement = UIConstants.spacingMedium,
14 | modifier = Modifier.padding(UIConstants.paddingMedium)
15 | ) {
16 | PropertyView(name = "ID", value = setupToken.id)
17 | PropertyView(name = "Customer ID", value = setupToken.customerId)
18 | PropertyView(name = "Status", value = setupToken.status)
19 | setupToken.verificationStatus?.let { verificationStatus ->
20 | PropertyView(name = "Verification Status", value = verificationStatus)
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/Demo/src/main/java/com/paypal/android/uishared/components/CardVaultResultView.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android.uishared.components
2 |
3 | import androidx.compose.foundation.layout.Column
4 | import androidx.compose.foundation.layout.padding
5 | import androidx.compose.runtime.Composable
6 | import androidx.compose.ui.Modifier
7 | import com.paypal.android.ui.approveorder.SetupTokenInfo
8 | import com.paypal.android.utils.UIConstants
9 |
10 | @Composable
11 | fun CardVaultResultView(result: SetupTokenInfo) {
12 | Column(
13 | verticalArrangement = UIConstants.spacingMedium,
14 | modifier = Modifier.padding(UIConstants.paddingMedium)
15 | ) {
16 | PropertyView(name = "Setup Token ID", value = result.setupTokenId)
17 | PropertyView(name = "Status", value = result.status)
18 | val didAttemptText = if (result.didAttemptThreeDSecureAuthentication) "YES" else "NO"
19 | PropertyView(name = "Did Attempt 3DS Authentication", value = didAttemptText)
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/Demo/src/main/java/com/paypal/android/uishared/components/CreateOrderForm.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android.uishared.components
2 |
3 | import androidx.compose.foundation.layout.Column
4 | import androidx.compose.foundation.layout.fillMaxSize
5 | import androidx.compose.material3.MaterialTheme
6 | import androidx.compose.material3.Surface
7 | import androidx.compose.runtime.Composable
8 | import androidx.compose.ui.Modifier
9 | import androidx.compose.ui.res.stringResource
10 | import androidx.compose.ui.tooling.preview.Preview
11 | import com.paypal.android.R
12 | import com.paypal.android.api.model.OrderIntent
13 | import com.paypal.android.utils.UIConstants
14 |
15 | @Composable
16 | fun CreateOrderForm(
17 | orderIntent: OrderIntent = OrderIntent.AUTHORIZE,
18 | onOrderIntentChange: (OrderIntent) -> Unit = {},
19 | ) {
20 | Column(
21 | verticalArrangement = UIConstants.spacingMedium
22 | ) {
23 | EnumOptionList(
24 | title = stringResource(id = R.string.intent_title),
25 | stringArrayResId = R.array.intent_options,
26 | onSelectedOptionChange = { onOrderIntentChange(it) },
27 | selectedOption = orderIntent
28 | )
29 | }
30 | }
31 |
32 | @Preview
33 | @Composable
34 | fun CreateOrderFormPreview() {
35 | MaterialTheme {
36 | Surface(modifier = Modifier.fillMaxSize()) {
37 | CreateOrderForm()
38 | }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/Demo/src/main/java/com/paypal/android/uishared/components/CreateOrderWithVaultOptionForm.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android.uishared.components
2 |
3 | import androidx.compose.foundation.layout.Column
4 | import androidx.compose.foundation.layout.fillMaxSize
5 | import androidx.compose.material3.MaterialTheme
6 | import androidx.compose.material3.Surface
7 | import androidx.compose.runtime.Composable
8 | import androidx.compose.ui.Modifier
9 | import androidx.compose.ui.res.stringResource
10 | import androidx.compose.ui.tooling.preview.Preview
11 | import com.paypal.android.R
12 | import com.paypal.android.api.model.OrderIntent
13 | import com.paypal.android.uishared.enums.StoreInVaultOption
14 | import com.paypal.android.utils.UIConstants
15 |
16 | @Composable
17 | fun CreateOrderWithVaultOptionForm(
18 | orderIntent: OrderIntent = OrderIntent.AUTHORIZE,
19 | shouldVault: StoreInVaultOption = StoreInVaultOption.NO,
20 | onShouldVaultChanged: (StoreInVaultOption) -> Unit = {},
21 | onIntentOptionChanged: (OrderIntent) -> Unit = {},
22 | ) {
23 | Column(
24 | verticalArrangement = UIConstants.spacingMedium
25 | ) {
26 | EnumOptionList(
27 | title = stringResource(id = R.string.intent_title),
28 | stringArrayResId = R.array.intent_options,
29 | onSelectedOptionChange = { onIntentOptionChanged(it) },
30 | selectedOption = orderIntent
31 | )
32 | EnumOptionList(
33 | title = stringResource(id = R.string.store_in_vault),
34 | stringArrayResId = R.array.store_in_vault_options,
35 | onSelectedOptionChange = { onShouldVaultChanged(it) },
36 | selectedOption = shouldVault
37 | )
38 | }
39 | }
40 |
41 | @Preview
42 | @Composable
43 | fun CreateOrderWithVaultOptionFormPreview() {
44 | MaterialTheme {
45 | Surface(modifier = Modifier.fillMaxSize()) {
46 | CreateOrderWithVaultOptionForm()
47 | }
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/Demo/src/main/java/com/paypal/android/uishared/components/DemoAppTopBar.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android.uishared.components
2 |
3 | import androidx.compose.foundation.background
4 | import androidx.compose.material.icons.Icons
5 | import androidx.compose.material.icons.filled.ArrowBack
6 | import androidx.compose.material3.CenterAlignedTopAppBar
7 | import androidx.compose.material3.ExperimentalMaterial3Api
8 | import androidx.compose.material3.Icon
9 | import androidx.compose.material3.IconButton
10 | import androidx.compose.material3.MaterialTheme
11 | import androidx.compose.material3.Text
12 | import androidx.compose.material3.TopAppBarDefaults
13 | import androidx.compose.runtime.Composable
14 | import androidx.compose.ui.Modifier
15 |
16 | @ExperimentalMaterial3Api
17 | @Composable
18 | fun DemoAppTopBar(
19 | title: String,
20 | shouldDisplayBackButton: Boolean,
21 | onBackButtonClick: () -> Unit
22 | ) {
23 | CenterAlignedTopAppBar(
24 | title = {
25 | Text(text = title)
26 | },
27 | colors = TopAppBarDefaults.topAppBarColors(
28 | containerColor = MaterialTheme.colorScheme.surface,
29 | titleContentColor = MaterialTheme.colorScheme.onSurface
30 | ),
31 | navigationIcon = {
32 | // Ref: https://stackoverflow.com/a/70409412
33 | if (shouldDisplayBackButton) {
34 | IconButton(onClick = onBackButtonClick) {
35 | Icon(
36 | imageVector = Icons.Filled.ArrowBack,
37 | contentDescription = "Back"
38 | )
39 | }
40 | }
41 | },
42 | modifier = Modifier
43 | .background(MaterialTheme.colorScheme.surface)
44 | )
45 | }
46 |
--------------------------------------------------------------------------------
/Demo/src/main/java/com/paypal/android/uishared/components/ErrorView.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android.uishared.components
2 |
3 | import androidx.compose.foundation.layout.Column
4 | import androidx.compose.foundation.layout.fillMaxSize
5 | import androidx.compose.foundation.layout.padding
6 | import androidx.compose.material3.MaterialTheme
7 | import androidx.compose.material3.Surface
8 | import androidx.compose.runtime.Composable
9 | import androidx.compose.ui.Modifier
10 | import androidx.compose.ui.tooling.preview.Preview
11 | import com.paypal.android.corepayments.PayPalSDKError
12 | import com.paypal.android.utils.UIConstants
13 |
14 | @Composable
15 | fun ErrorView(error: Exception) {
16 | Column(
17 | verticalArrangement = UIConstants.spacingMedium,
18 | modifier = Modifier.padding(UIConstants.paddingMedium)
19 | ) {
20 | if (error is PayPalSDKError) {
21 | PropertyView(name = "Error Code", value = "${error.code}")
22 | PropertyView(name = "Error Description", value = error.errorDescription)
23 | PropertyView(name = "Correlation ID", value = error.correlationId)
24 | } else {
25 | PropertyView(name = "Message", value = error.message)
26 | }
27 | }
28 | }
29 |
30 | @Preview
31 | @Composable
32 | fun ErrorViewActionColumnPreview() {
33 | MaterialTheme {
34 | Surface(modifier = Modifier.fillMaxSize()) {
35 | ErrorView(error = java.lang.Exception("Fake Exception"))
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/Demo/src/main/java/com/paypal/android/uishared/components/InfoColumn.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android.uishared.components
2 |
3 | import androidx.compose.foundation.background
4 | import androidx.compose.foundation.layout.Column
5 | import androidx.compose.foundation.layout.Row
6 | import androidx.compose.foundation.layout.fillMaxWidth
7 | import androidx.compose.foundation.layout.padding
8 | import androidx.compose.material3.Card
9 | import androidx.compose.material3.MaterialTheme
10 | import androidx.compose.material3.Text
11 | import androidx.compose.runtime.Composable
12 | import androidx.compose.ui.Modifier
13 | import androidx.compose.ui.text.style.TextAlign
14 | import androidx.compose.ui.tooling.preview.Preview
15 | import com.paypal.android.utils.UIConstants
16 |
17 | @Composable
18 | fun InfoColumn(
19 | title: String,
20 | modifier: Modifier = Modifier,
21 | content: @Composable () -> Unit = {},
22 | ) {
23 | Card(
24 | modifier = modifier
25 | ) {
26 | Row(
27 | modifier = Modifier
28 | .background(MaterialTheme.colorScheme.inverseSurface),
29 | ) {
30 | Text(
31 | text = title,
32 | color = MaterialTheme.colorScheme.inverseOnSurface,
33 | style = MaterialTheme.typography.bodyLarge,
34 | textAlign = TextAlign.Center,
35 | modifier = Modifier
36 | .padding(UIConstants.paddingMedium)
37 | .fillMaxWidth()
38 | )
39 | }
40 | Column {
41 | content()
42 | }
43 | }
44 | }
45 |
46 | @Preview
47 | @Composable
48 | fun InfoColumnPreview() {
49 | MaterialTheme {
50 | Column {
51 | InfoColumn(
52 | title = "Sample Title"
53 | ) {
54 | Text(
55 | text = "Sample Text",
56 | modifier = Modifier.padding(UIConstants.paddingLarge)
57 | )
58 | }
59 | }
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/Demo/src/main/java/com/paypal/android/uishared/components/IntSlider.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android.uishared.components
2 |
3 | import androidx.compose.material3.Slider
4 | import androidx.compose.material3.SliderColors
5 | import androidx.compose.runtime.Composable
6 |
7 | @Composable
8 | fun IntSlider(
9 | value: Int,
10 | onValueChange: (Int) -> Unit,
11 | valueRange: ClosedRange,
12 | colors: SliderColors,
13 | steps: Int,
14 | ) {
15 | Slider(
16 | value = value.toFloat(),
17 | valueRange = valueRange.start.toFloat()..valueRange.endInclusive.toFloat(),
18 | steps = steps,
19 | colors = colors,
20 | onValueChange = { onValueChange(it.toInt()) }
21 | )
22 | }
23 |
--------------------------------------------------------------------------------
/Demo/src/main/java/com/paypal/android/uishared/components/OrderView.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android.uishared.components
2 |
3 | import androidx.compose.foundation.layout.Column
4 | import androidx.compose.foundation.layout.fillMaxWidth
5 | import androidx.compose.foundation.layout.padding
6 | import androidx.compose.material3.MaterialTheme
7 | import androidx.compose.material3.Surface
8 | import androidx.compose.material3.Text
9 | import androidx.compose.runtime.Composable
10 | import androidx.compose.ui.Modifier
11 | import androidx.compose.ui.tooling.preview.Preview
12 | import com.paypal.android.api.model.Order
13 | import com.paypal.android.utils.UIConstants
14 |
15 | @Composable
16 | fun OrderView(order: Order, title: String? = null) {
17 | Column(
18 | modifier = Modifier
19 | .fillMaxWidth()
20 | .padding(UIConstants.paddingMedium)
21 | ) {
22 | title?.let { text ->
23 | Text(
24 | text = text,
25 | style = MaterialTheme.typography.titleLarge,
26 | modifier = Modifier.padding(vertical = UIConstants.paddingMedium)
27 | )
28 | }
29 | Column(
30 | verticalArrangement = UIConstants.spacingMedium,
31 | modifier = Modifier
32 | .fillMaxWidth()
33 | ) {
34 | PropertyView(name = "ID", value = order.id)
35 | order.intent?.let { PropertyView(name = "Intent", value = it) }
36 | order.status?.let { PropertyView(name = "Status", value = it) }
37 | order.cardLast4?.let { PropertyView(name = "Card Last 4", value = order.cardLast4) }
38 | order.cardBrand?.let { PropertyView(name = "Card Brand", value = order.cardBrand) }
39 | order.vaultId?.let {
40 | PropertyView(name = "Vault Id / Payment Token", value = order.vaultId)
41 | }
42 | order.customerId?.let {
43 | PropertyView(name = "Customer Vault Id", value = order.customerId)
44 | }
45 | }
46 | }
47 | }
48 |
49 | @Preview
50 | @Composable
51 | fun OrderPreview() {
52 | MaterialTheme {
53 | Surface(modifier = Modifier.fillMaxWidth()) {
54 | OrderView(Order(), "Sample Title")
55 | }
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/Demo/src/main/java/com/paypal/android/uishared/components/PayPalPaymentTokenView.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android.uishared.components
2 |
3 | import androidx.compose.foundation.layout.Column
4 | import androidx.compose.foundation.layout.padding
5 | import androidx.compose.runtime.Composable
6 | import androidx.compose.ui.Modifier
7 | import com.paypal.android.api.model.PayPalPaymentToken
8 | import com.paypal.android.utils.UIConstants
9 |
10 | @Composable
11 | fun PayPalPaymentTokenView(paymentToken: PayPalPaymentToken) {
12 | Column(
13 | verticalArrangement = UIConstants.spacingMedium,
14 | modifier = Modifier.padding(UIConstants.paddingMedium)
15 | ) {
16 | PropertyView(name = "ID", value = paymentToken.id)
17 | PropertyView(name = "Customer ID", value = paymentToken.customerId)
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/Demo/src/main/java/com/paypal/android/uishared/components/PayPalSetupTokenView.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android.uishared.components
2 |
3 | import androidx.compose.foundation.layout.Column
4 | import androidx.compose.foundation.layout.padding
5 | import androidx.compose.runtime.Composable
6 | import androidx.compose.ui.Modifier
7 | import com.paypal.android.api.model.PayPalSetupToken
8 | import com.paypal.android.utils.UIConstants
9 |
10 | @Composable
11 | fun PayPalSetupTokenView(setupToken: PayPalSetupToken) {
12 | Column(
13 | verticalArrangement = UIConstants.spacingMedium,
14 | modifier = Modifier.padding(UIConstants.paddingMedium)
15 | ) {
16 | PropertyView(name = "ID", value = setupToken.id)
17 | PropertyView(name = "Customer ID", value = setupToken.customerId)
18 | PropertyView(name = "Status", value = setupToken.status)
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/Demo/src/main/java/com/paypal/android/uishared/components/PropertyView.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android.uishared.components
2 |
3 | import androidx.compose.foundation.layout.Column
4 | import androidx.compose.material3.MaterialTheme
5 | import androidx.compose.material3.Text
6 | import androidx.compose.runtime.Composable
7 | import com.paypal.android.utils.UIConstants
8 |
9 | @Composable
10 | fun PropertyView(name: String, value: String?) {
11 | Column(
12 | verticalArrangement = UIConstants.spacingExtraSmall
13 | ) {
14 | Text(
15 | text = name,
16 | style = MaterialTheme.typography.titleMedium,
17 | )
18 | Text(text = value ?: "UNSET")
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/Demo/src/main/java/com/paypal/android/uishared/components/UriView.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android.uishared.components
2 |
3 | import android.net.Uri
4 | import androidx.compose.foundation.layout.Arrangement
5 | import androidx.compose.foundation.layout.Column
6 | import androidx.compose.foundation.layout.Row
7 | import androidx.compose.foundation.layout.padding
8 | import androidx.compose.material3.Text
9 | import androidx.compose.runtime.Composable
10 | import androidx.compose.ui.Modifier
11 | import com.paypal.android.utils.UIConstants
12 |
13 | @Composable
14 | fun UriView(uri: Uri) {
15 | Row(
16 | horizontalArrangement = Arrangement.spacedBy(UIConstants.paddingSmall),
17 | modifier = Modifier
18 | .padding(top = UIConstants.paddingSmall)
19 | ) {
20 | Text("Scheme:")
21 | Text(uri.scheme ?: "NOT SET")
22 | }
23 | Row(
24 | horizontalArrangement = Arrangement.spacedBy(UIConstants.paddingSmall),
25 | modifier = Modifier
26 | .padding(top = UIConstants.paddingSmall)
27 | ) {
28 | Text("Host:")
29 | Text(uri.host ?: "NOT SET")
30 | }
31 | Row(
32 | horizontalArrangement = Arrangement.spacedBy(UIConstants.paddingSmall),
33 | modifier = Modifier
34 | .padding(top = UIConstants.paddingSmall)
35 | ) {
36 | Text("Path:")
37 | Text(uri.path ?: "NOT SET")
38 | }
39 | val queryParameterNames = uri.queryParameterNames ?: emptySet()
40 | if (queryParameterNames.isNotEmpty()) {
41 | Text(
42 | text = "Params:",
43 | modifier = Modifier
44 | .padding(top = UIConstants.paddingSmall)
45 | )
46 | Column(
47 | modifier = Modifier.padding(UIConstants.paddingSmall)
48 | ) {
49 | for (paramName in queryParameterNames) {
50 | Row(
51 | horizontalArrangement = Arrangement.spacedBy(UIConstants.paddingSmall)
52 | ) {
53 | Text("$paramName:")
54 | Text(uri.getQueryParameter(paramName) ?: "PRESENT BUT NOT SET")
55 | }
56 | }
57 | }
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/Demo/src/main/java/com/paypal/android/uishared/effects/NavDestinationChangeDisposableEffect.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android.uishared.effects
2 |
3 | import androidx.compose.runtime.Composable
4 | import androidx.compose.runtime.DisposableEffect
5 | import androidx.navigation.NavController
6 |
7 | @Composable
8 | fun NavDestinationChangeDisposableEffect(
9 | navController: NavController,
10 | onDestinationChange: (controller: NavController) -> Unit
11 | ) {
12 | // Ref: https://stackoverflow.com/a/68700967
13 | DisposableEffect(navController) {
14 | val listener = NavController.OnDestinationChangedListener { controller, _, _ ->
15 | onDestinationChange(controller)
16 | }
17 | navController.addOnDestinationChangedListener(listener)
18 | onDispose {
19 | navController.removeOnDestinationChangedListener(listener)
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/Demo/src/main/java/com/paypal/android/uishared/enums/StoreInVaultOption.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android.uishared.enums
2 |
3 | enum class StoreInVaultOption {
4 | ON_SUCCESS, NO
5 | }
6 |
--------------------------------------------------------------------------------
/Demo/src/main/java/com/paypal/android/uishared/state/ActionState.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android.uishared.state
2 |
3 | sealed class ActionState {
4 | object Idle : ActionState()
5 | object Loading : ActionState()
6 | data class Success(val value: S) : ActionState()
7 | data class Failure(val value: E) : ActionState()
8 |
9 | val isComplete: Boolean
10 | get() = (this is Success) || (this is Failure)
11 | }
12 |
--------------------------------------------------------------------------------
/Demo/src/main/java/com/paypal/android/uishared/state/CompletedActionState.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android.uishared.state
2 |
3 | sealed class CompletedActionState {
4 | data class Success(val value: S) : CompletedActionState()
5 | data class Failure(val value: E) : CompletedActionState()
6 | }
7 |
--------------------------------------------------------------------------------
/Demo/src/main/java/com/paypal/android/usecase/CompleteOrderUseCase.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android.usecase
2 |
3 | import com.paypal.android.api.model.Order
4 | import com.paypal.android.api.model.OrderIntent
5 | import com.paypal.android.api.services.SDKSampleServerAPI
6 | import com.paypal.android.api.services.SDKSampleServerResult
7 | import kotlinx.coroutines.Dispatchers
8 | import kotlinx.coroutines.withContext
9 | import javax.inject.Inject
10 |
11 | class CompleteOrderUseCase @Inject constructor(
12 | private val sdkSampleServerAPI: SDKSampleServerAPI
13 | ) {
14 |
15 | suspend operator fun invoke(
16 | orderId: String,
17 | intent: OrderIntent,
18 | clientMetadataId: String
19 | ): SDKSampleServerResult = withContext(Dispatchers.IO) {
20 | when (intent) {
21 | OrderIntent.CAPTURE ->
22 | sdkSampleServerAPI.captureOrder(orderId, clientMetadataId)
23 |
24 | OrderIntent.AUTHORIZE ->
25 | sdkSampleServerAPI.authorizeOrder(orderId, clientMetadataId)
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/Demo/src/main/java/com/paypal/android/usecase/CreateCardPaymentTokenUseCase.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android.usecase
2 |
3 | import com.google.gson.JsonObject
4 | import com.google.gson.JsonParser
5 | import com.paypal.android.api.model.CardPaymentToken
6 | import com.paypal.android.api.model.CardSetupToken
7 | import com.paypal.android.api.services.SDKSampleServerAPI
8 | import com.paypal.android.api.services.SDKSampleServerResult
9 | import kotlinx.coroutines.Dispatchers
10 | import kotlinx.coroutines.withContext
11 | import javax.inject.Inject
12 |
13 | class CreateCardPaymentTokenUseCase @Inject constructor(
14 | private val sdkSampleServerAPI: SDKSampleServerAPI
15 | ) {
16 |
17 | suspend operator fun invoke(setupToken: CardSetupToken): SDKSampleServerResult =
18 | withContext(Dispatchers.IO) {
19 | // language=JSON
20 | val request = """
21 | {
22 | "payment_source": {
23 | "token": {
24 | "id": "${setupToken.id}",
25 | "type": "SETUP_TOKEN"
26 | }
27 | }
28 | }
29 | """
30 |
31 | val requestJson = JsonParser.parseString(request) as JsonObject
32 | sdkSampleServerAPI.createPaymentToken(requestJson)
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/Demo/src/main/java/com/paypal/android/usecase/CreateCardSetupTokenUseCase.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android.usecase
2 |
3 | import com.google.gson.JsonObject
4 | import com.google.gson.JsonParser
5 | import com.paypal.android.api.model.CardSetupToken
6 | import com.paypal.android.api.services.SDKSampleServerAPI
7 | import com.paypal.android.api.services.SDKSampleServerResult
8 | import com.paypal.android.cardpayments.threedsecure.SCA
9 | import kotlinx.coroutines.Dispatchers
10 | import kotlinx.coroutines.withContext
11 | import javax.inject.Inject
12 |
13 | class CreateCardSetupTokenUseCase @Inject constructor(
14 | private val sdkSampleServerAPI: SDKSampleServerAPI
15 | ) {
16 |
17 | suspend operator fun invoke(sca: SCA): SDKSampleServerResult =
18 | withContext(Dispatchers.IO) {
19 | // create a payment token with an empty card attribute; the merchant app will
20 | // provide the card's details through the SDK
21 | // language=JSON
22 | val request = """
23 | {
24 | "payment_source": {
25 | "card": {
26 | "verification_method": "${sca.name}",
27 | "experience_context": {
28 | "return_url": "com.paypal.android.demo://vault/success",
29 | "cancel_url": "com.paypal.android.demo://vault/cancel"
30 | }
31 | }
32 | }
33 | }
34 | """
35 | val jsonOrder = JsonParser.parseString(request) as JsonObject
36 | sdkSampleServerAPI.createSetupToken(jsonOrder)
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/Demo/src/main/java/com/paypal/android/usecase/CreateOrderUseCase.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android.usecase
2 |
3 | import com.paypal.android.api.model.Order
4 | import com.paypal.android.api.services.SDKSampleServerAPI
5 | import com.paypal.android.api.services.SDKSampleServerResult
6 | import com.paypal.android.models.OrderRequest
7 | import kotlinx.coroutines.Dispatchers
8 | import kotlinx.coroutines.withContext
9 | import org.json.JSONArray
10 | import org.json.JSONObject
11 | import javax.inject.Inject
12 |
13 | class CreateOrderUseCase @Inject constructor(
14 | private val sdkSampleServerAPI: SDKSampleServerAPI
15 | ) {
16 |
17 | suspend operator fun invoke(request: OrderRequest): SDKSampleServerResult =
18 | withContext(Dispatchers.IO) {
19 | val amountJSON = JSONObject()
20 | .put("currency_code", "USD")
21 | .put("value", "10.99")
22 |
23 | val purchaseUnitJSON = JSONObject()
24 | .put("amount", amountJSON)
25 |
26 | val orderRequest = JSONObject()
27 | .put("intent", request.intent)
28 | .put("purchase_units", JSONArray().put(purchaseUnitJSON))
29 |
30 | if (request.shouldVault) {
31 | val vaultJSON = JSONObject()
32 | .put("store_in_vault", "ON_SUCCESS")
33 |
34 | val cardAttributesJSON = JSONObject()
35 | .put("vault", vaultJSON)
36 |
37 | val cardJSON = JSONObject()
38 | .put("attributes", cardAttributesJSON)
39 |
40 | val paymentSourceJSON = JSONObject()
41 | .put("card", cardJSON)
42 |
43 | orderRequest.put("payment_source", paymentSourceJSON)
44 | }
45 | sdkSampleServerAPI.createOrder(orderRequest)
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/Demo/src/main/java/com/paypal/android/usecase/CreatePayPalPaymentTokenUseCase.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android.usecase
2 |
3 | import com.google.gson.JsonObject
4 | import com.google.gson.JsonParser
5 | import com.paypal.android.api.model.PayPalPaymentToken
6 | import com.paypal.android.api.model.PayPalSetupToken
7 | import com.paypal.android.api.services.SDKSampleServerAPI
8 | import com.paypal.android.api.services.SDKSampleServerResult
9 | import kotlinx.coroutines.Dispatchers
10 | import kotlinx.coroutines.withContext
11 | import javax.inject.Inject
12 |
13 | class CreatePayPalPaymentTokenUseCase @Inject constructor(
14 | private val sdkSampleServerAPI: SDKSampleServerAPI
15 | ) {
16 | suspend operator fun invoke(setupToken: PayPalSetupToken): SDKSampleServerResult =
17 | withContext(Dispatchers.IO) {
18 | // language=JSON
19 | val request = """
20 | {
21 | "payment_source": {
22 | "token": {
23 | "id": "${setupToken.id}",
24 | "type": "SETUP_TOKEN"
25 | }
26 | }
27 | }
28 | """
29 |
30 | val requestJson = JsonParser.parseString(request) as JsonObject
31 | sdkSampleServerAPI.createPayPalPaymentToken(requestJson)
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/Demo/src/main/java/com/paypal/android/usecase/CreatePayPalSetupTokenUseCase.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android.usecase
2 |
3 | import com.google.gson.JsonObject
4 | import com.google.gson.JsonParser
5 | import com.paypal.android.api.model.PayPalSetupToken
6 | import com.paypal.android.api.services.SDKSampleServerAPI
7 | import com.paypal.android.api.services.SDKSampleServerResult
8 | import kotlinx.coroutines.Dispatchers
9 | import kotlinx.coroutines.withContext
10 | import javax.inject.Inject
11 |
12 | class CreatePayPalSetupTokenUseCase @Inject constructor(
13 | private val sdkSampleServerAPI: SDKSampleServerAPI
14 | ) {
15 |
16 | suspend operator fun invoke(): SDKSampleServerResult =
17 | withContext(Dispatchers.IO) {
18 | // language=JSON
19 | val request = """
20 | {
21 | "payment_source": {
22 | "paypal": {
23 | "usage_type": "MERCHANT",
24 | "experience_context": {
25 | "vault_instruction": "ON_PAYER_APPROVAL",
26 | "return_url": "com.paypal.android.demo://vault/success",
27 | "cancel_url": "com.paypal.android.demo://vault/cancel"
28 | }
29 | }
30 | }
31 | }
32 | """
33 |
34 | // Ref: https://stackoverflow.com/a/19610814
35 | val body = request.replace("\\/", "/")
36 |
37 | val jsonOrder = JsonParser.parseString(body) as JsonObject
38 | sdkSampleServerAPI.createPayPalSetupToken(jsonOrder)
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/Demo/src/main/java/com/paypal/android/usecase/GetClientIdUseCase.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android.usecase
2 |
3 | import com.paypal.android.api.services.SDKSampleServerAPI
4 | import com.paypal.android.api.services.SDKSampleServerResult
5 | import kotlinx.coroutines.Dispatchers
6 | import kotlinx.coroutines.withContext
7 | import javax.inject.Inject
8 |
9 | class GetClientIdUseCase @Inject constructor(
10 | private val sdkSampleServerAPI: SDKSampleServerAPI
11 | ) {
12 | suspend operator fun invoke(): SDKSampleServerResult = withContext(Dispatchers.IO) {
13 | sdkSampleServerAPI.fetchClientId()
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/Demo/src/main/java/com/paypal/android/usecase/GetSetupTokenUseCase.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android.usecase
2 |
3 | import com.paypal.android.api.model.CardSetupToken
4 | import com.paypal.android.api.services.SDKSampleServerAPI
5 | import com.paypal.android.api.services.SDKSampleServerResult
6 | import kotlinx.coroutines.Dispatchers
7 | import kotlinx.coroutines.withContext
8 | import javax.inject.Inject
9 |
10 | class GetSetupTokenUseCase @Inject constructor(
11 | private val sdkSampleServerAPI: SDKSampleServerAPI
12 | ) {
13 |
14 | suspend operator fun invoke(setupTokenId: String): SDKSampleServerResult =
15 | withContext(Dispatchers.IO) {
16 | sdkSampleServerAPI.getSetupToken(setupTokenId)
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/Demo/src/main/java/com/paypal/android/utils/ContextExt.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android.utils
2 |
3 | import android.content.Context
4 | import android.content.ContextWrapper
5 | import androidx.activity.ComponentActivity
6 |
7 | // Ref: https://stackoverflow.com/a/68423182
8 | fun Context.getActivityOrNull(): ComponentActivity? = when (this) {
9 | is ComponentActivity -> this
10 | is ContextWrapper -> baseContext.getActivityOrNull()
11 | else -> null
12 | }
13 |
--------------------------------------------------------------------------------
/Demo/src/main/java/com/paypal/android/utils/OnLifecycleOwnerResumeEffect.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android.utils
2 |
3 | import androidx.compose.runtime.Composable
4 | import androidx.compose.runtime.LaunchedEffect
5 | import androidx.compose.runtime.collectAsState
6 | import androidx.compose.runtime.getValue
7 | import androidx.compose.ui.platform.LocalLifecycleOwner
8 | import androidx.lifecycle.Lifecycle
9 |
10 | @Composable
11 | fun OnLifecycleOwnerResumeEffect(callback: () -> Unit) {
12 | // Ref: https://stackoverflow.com/a/66549433
13 | val lifecycleOwner = LocalLifecycleOwner.current
14 | val lifecycleState by lifecycleOwner.lifecycle.currentStateFlow.collectAsState()
15 | LaunchedEffect(lifecycleState) {
16 | if (lifecycleState == Lifecycle.State.RESUMED) {
17 | callback()
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/Demo/src/main/java/com/paypal/android/utils/OnNewIntentEffect.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android.utils
2 |
3 | import android.content.Intent
4 | import androidx.compose.runtime.Composable
5 | import androidx.compose.runtime.DisposableEffect
6 | import androidx.compose.ui.platform.LocalContext
7 | import androidx.core.util.Consumer
8 |
9 | @Composable
10 | fun OnNewIntentEffect(callback: (newIntent: Intent) -> Unit) {
11 | val context = LocalContext.current
12 | // pass "Unit" to register listener only once
13 | DisposableEffect(Unit) {
14 | val listener = Consumer { newIntent ->
15 | callback(newIntent)
16 | }
17 | context.getActivityOrNull()?.addOnNewIntentListener(listener)
18 | onDispose {
19 | context.getActivityOrNull()?.removeOnNewIntentListener(listener)
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/Demo/src/main/java/com/paypal/android/utils/UIConstants.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android.utils
2 |
3 | import androidx.compose.foundation.layout.Arrangement
4 | import androidx.compose.ui.unit.dp
5 |
6 | object UIConstants {
7 | private const val slideInOffsetPercentY = 0.05
8 | fun getSlideInStartOffsetY(fullHeightOfComposable: Int) =
9 | (fullHeightOfComposable * slideInOffsetPercentY).toInt()
10 |
11 | // Ref: https://support.google.com/accessibility/android/answer/7101858
12 | val minimumTouchSize = 48.dp
13 | val stepNumberBackgroundSize = 40.dp
14 | val buttonCornerRadius = 16.dp
15 | val chevronSize = 16.dp
16 | val progressIndicatorSize = 32.dp
17 |
18 | val paddingExtraSmall = 4.dp
19 | val paddingSmall = 8.dp
20 | val paddingMedium = 16.dp
21 | val paddingLarge = 24.dp
22 |
23 | val spacingExtraSmall = Arrangement.spacedBy(paddingExtraSmall)
24 | val spacingSmall = Arrangement.spacedBy(paddingSmall)
25 | val spacingMedium = Arrangement.spacedBy(paddingMedium)
26 | val spacingLarge = Arrangement.spacedBy(paddingLarge)
27 | }
28 |
--------------------------------------------------------------------------------
/Demo/src/main/play/default-language.txt:
--------------------------------------------------------------------------------
1 | en-US
2 |
--------------------------------------------------------------------------------
/Demo/src/main/play/listings/en-US/title.txt:
--------------------------------------------------------------------------------
1 | PayPal SDK
2 |
--------------------------------------------------------------------------------
/Demo/src/main/play/release-notes/en-US/internal.txt:
--------------------------------------------------------------------------------
1 | Test Release
2 |
--------------------------------------------------------------------------------
/Demo/src/main/res/drawable/chevron.xml:
--------------------------------------------------------------------------------
1 |
6 |
13 |
14 |
--------------------------------------------------------------------------------
/Demo/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/Demo/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/Demo/src/main/res/mipmap-hdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/paypal/paypal-android/9d77a9e760518e586a64c135ad7f225e4293fb20/Demo/src/main/res/mipmap-hdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/Demo/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/paypal/paypal-android/9d77a9e760518e586a64c135ad7f225e4293fb20/Demo/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp
--------------------------------------------------------------------------------
/Demo/src/main/res/mipmap-hdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/paypal/paypal-android/9d77a9e760518e586a64c135ad7f225e4293fb20/Demo/src/main/res/mipmap-hdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/Demo/src/main/res/mipmap-mdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/paypal/paypal-android/9d77a9e760518e586a64c135ad7f225e4293fb20/Demo/src/main/res/mipmap-mdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/Demo/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/paypal/paypal-android/9d77a9e760518e586a64c135ad7f225e4293fb20/Demo/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp
--------------------------------------------------------------------------------
/Demo/src/main/res/mipmap-mdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/paypal/paypal-android/9d77a9e760518e586a64c135ad7f225e4293fb20/Demo/src/main/res/mipmap-mdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/Demo/src/main/res/mipmap-xhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/paypal/paypal-android/9d77a9e760518e586a64c135ad7f225e4293fb20/Demo/src/main/res/mipmap-xhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/Demo/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/paypal/paypal-android/9d77a9e760518e586a64c135ad7f225e4293fb20/Demo/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp
--------------------------------------------------------------------------------
/Demo/src/main/res/mipmap-xhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/paypal/paypal-android/9d77a9e760518e586a64c135ad7f225e4293fb20/Demo/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/Demo/src/main/res/mipmap-xxhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/paypal/paypal-android/9d77a9e760518e586a64c135ad7f225e4293fb20/Demo/src/main/res/mipmap-xxhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/Demo/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/paypal/paypal-android/9d77a9e760518e586a64c135ad7f225e4293fb20/Demo/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp
--------------------------------------------------------------------------------
/Demo/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/paypal/paypal-android/9d77a9e760518e586a64c135ad7f225e4293fb20/Demo/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/Demo/src/main/res/mipmap-xxxhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/paypal/paypal-android/9d77a9e760518e586a64c135ad7f225e4293fb20/Demo/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/Demo/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/paypal/paypal-android/9d77a9e760518e586a64c135ad7f225e4293fb20/Demo/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp
--------------------------------------------------------------------------------
/Demo/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/paypal/paypal-android/9d77a9e760518e586a64c135ad7f225e4293fb20/Demo/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/Demo/src/main/res/values-night/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
16 |
--------------------------------------------------------------------------------
/Demo/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #FF000000
4 | #FFFFFFFF
5 | #FF00457C
6 | #FF0079C1
7 | #FF36454f
8 |
--------------------------------------------------------------------------------
/Demo/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | AndroidSDK
3 |
4 |
5 | Approve Order
6 | Vault
7 | PayPal Web
8 | PayPal Web Vault
9 | PayPal Buttons
10 | PayPal Buttons - XML
11 |
12 |
13 | CARD NUMBER
14 | EXP. DATE
15 | SEC. CODE
16 |
17 | SCA
18 |
19 | - SCA_ALWAYS
20 | - SCA_WHEN_REQUIRED
21 |
22 |
23 | Button Preview
24 | Button Options
25 | Button Type
26 | Button Color
27 | Button Label
28 | Button Shape
29 | Button Size
30 |
31 | INTENT
32 |
33 | - AUTHORIZE
34 | - CAPTURE
35 |
36 |
37 | STORE IN VAULT
38 |
39 | - NO
40 | - ON_SUCCESS
41 |
42 |
43 | FUNDING SOURCE
44 |
45 | - PAYPAL_CREDIT
46 | - PAY_LATER
47 | - PAYPAL
48 |
49 |
50 |
51 |
--------------------------------------------------------------------------------
/Demo/src/main/res/values/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
16 |
--------------------------------------------------------------------------------
/Demo/src/main/res/xml/network_security_config.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/Demo/src/test/java/com/paypal/android/ExampleUnitTest.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android
2 |
3 | import junit.framework.TestCase.assertEquals
4 | import org.junit.Test
5 |
6 | /**
7 | * Example local unit test, which will execute on the development machine (host).
8 | *
9 | * See [testing documentation](http://d.android.com/tools/testing).
10 | */
11 | class ExampleUnitTest {
12 | @Test
13 | fun addition_isCorrect() {
14 | assertEquals(4, 2 + 2)
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/FraudProtection/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/FraudProtection/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | alias libs.plugins.android.library
3 | alias libs.plugins.kotlinAndroid
4 | }
5 |
6 | android {
7 | namespace 'com.paypal.android.fraudprotection'
8 | java {
9 | toolchain {
10 | languageVersion = modules.kotlinToolchainLanguageVersion
11 | }
12 | }
13 |
14 | defaultConfig {
15 | compileSdk modules.androidCompileSdk
16 | minSdkVersion modules.androidMinSdkVersion
17 | targetSdkVersion modules.androidTargetVersion
18 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
19 | consumerProguardFiles "consumer-rules.pro"
20 | }
21 |
22 | buildTypes {
23 | release {
24 | version = rootProject.version
25 | minifyEnabled false
26 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
27 | }
28 | }
29 |
30 | compileOptions {
31 | sourceCompatibility modules.sourceCompatibility
32 | targetCompatibility modules.targetCompatibility
33 | }
34 |
35 | kotlinOptions {
36 | jvmTarget = modules.kotlinJvmTarget
37 | }
38 |
39 | testOptions {
40 | unitTests.returnDefaultValues = true
41 | }
42 | namespace 'com.paypal.android.fraudprotection'
43 | }
44 |
45 | dependencies {
46 | implementation files('libs/android-magnessdk-5.5.1.jar')
47 |
48 | api project(':CorePayments')
49 | implementation libs.kotlin.stdLib
50 | implementation libs.androidx.coreKtx
51 | implementation libs.androidx.appcompat
52 | implementation libs.androidx.preferenceKtx
53 |
54 | testImplementation libs.junit
55 | testImplementation libs.mockk
56 | testImplementation libs.jsonAssert
57 | testImplementation libs.robolectric
58 |
59 | androidTestImplementation libs.androidx.test.junit
60 | androidTestImplementation libs.androidx.test.espresso
61 | }
62 |
63 |
64 | project.ext.name = "fraud-protection"
65 | project.ext.version = rootProject.version
66 | project.ext.pom_name = "PayPal SDK - Fraud Protection"
67 | project.ext.pom_desc = "Library for PayPal's fraud protection"
68 |
69 | apply from: rootProject.file("gradle/gradle-publish.gradle")
--------------------------------------------------------------------------------
/FraudProtection/consumer-rules.pro:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/paypal/paypal-android/9d77a9e760518e586a64c135ad7f225e4293fb20/FraudProtection/consumer-rules.pro
--------------------------------------------------------------------------------
/FraudProtection/libs/android-magnessdk-5.5.1.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/paypal/paypal-android/9d77a9e760518e586a64c135ad7f225e4293fb20/FraudProtection/libs/android-magnessdk-5.5.1.jar
--------------------------------------------------------------------------------
/FraudProtection/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
--------------------------------------------------------------------------------
/FraudProtection/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/FraudProtection/src/main/java/com/paypal/android/fraudprotection/CoreConfigMagnesExt.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android.fraudprotection
2 |
3 | import com.paypal.android.corepayments.CoreConfig
4 | import lib.android.paypal.com.magnessdk.Environment
5 |
6 | internal val CoreConfig.magnesEnvironment: Environment
7 | get() = when (environment) {
8 | com.paypal.android.corepayments.Environment.LIVE -> Environment.LIVE
9 | com.paypal.android.corepayments.Environment.SANDBOX -> Environment.SANDBOX
10 | }
11 |
--------------------------------------------------------------------------------
/FraudProtection/src/main/java/com/paypal/android/fraudprotection/PayPalDataCollectorRequest.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android.fraudprotection
2 |
3 | /**
4 | * Request object containing parameters to configure fraud protection data collection.
5 | *
6 | * @property [hasUserLocationConsent] informs the SDK if your application has obtained
7 | * consent from the user to collect location data in compliance with
8 | *
9 | * Google Play Developer Program policies
10 | * This flag enables PayPal to collect necessary information required for Fraud Detection and Risk Management.
11 | * @property [clientMetadataId] forward this data to your server when completing a transaction
12 | * @property [additionalData] additional metadata to link with data collection
13 | */
14 | data class PayPalDataCollectorRequest @JvmOverloads constructor(
15 | val hasUserLocationConsent: Boolean,
16 | val clientMetadataId: String? = null,
17 | val additionalData: Map? = null,
18 | )
19 |
--------------------------------------------------------------------------------
/FraudProtection/src/main/java/com/paypal/android/fraudprotection/SharedPreferenceUtils.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android.fraudprotection
2 |
3 | import android.content.Context
4 | import android.content.SharedPreferences
5 | import androidx.preference.PreferenceManager
6 |
7 | internal class SharedPreferenceUtils {
8 |
9 | companion object {
10 | val instance = SharedPreferenceUtils()
11 | }
12 |
13 | private fun getSharedPreferences(context: Context): SharedPreferences =
14 | PreferenceManager.getDefaultSharedPreferences(context)
15 |
16 | fun putString(context: Context, key: String, value: String) =
17 | getSharedPreferences(context)
18 | .edit()
19 | .putString(key, value)
20 | .apply()
21 |
22 | fun getString(context: Context, key: String, fallback: String?): String? =
23 | getSharedPreferences(context).getString(key, fallback) ?: fallback
24 | }
25 |
--------------------------------------------------------------------------------
/FraudProtection/src/main/java/com/paypal/android/fraudprotection/UUIDHelper.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android.fraudprotection
2 |
3 | import android.content.Context
4 | import java.util.UUID
5 |
6 | internal class UUIDHelper {
7 |
8 | companion object {
9 | private const val INSTALL_GUID: String = "InstallationGUID"
10 | }
11 |
12 | fun getInstallationGUID(context: Context): String {
13 | val existingGUID = SharedPreferenceUtils.instance.getString(context, INSTALL_GUID, null)
14 | return if (existingGUID != null) {
15 | existingGUID
16 | } else {
17 | val newGuid = UUID.randomUUID().toString()
18 | SharedPreferenceUtils.instance.putString(context, INSTALL_GUID, newGuid)
19 | newGuid
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/FraudProtection/src/test/java/com/paypal/android/fraudprotection/UUIDHelperUnitTest.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android.fraudprotection
2 |
3 | import io.mockk.every
4 | import io.mockk.just
5 | import io.mockk.mockk
6 | import io.mockk.mockkObject
7 | import io.mockk.mockkStatic
8 | import io.mockk.runs
9 | import io.mockk.unmockkAll
10 | import org.junit.After
11 | import org.junit.Test
12 | import org.junit.Assert.assertEquals
13 | import java.util.UUID
14 |
15 | class UUIDHelperUnitTest {
16 | @Test
17 | fun `when appGUID is not set, uuidHelper returns a new one`() {
18 | val mockUUID = "mock-uuid"
19 | mockkObject(SharedPreferenceUtils)
20 | mockkStatic(UUID::class)
21 | every { UUID.randomUUID().toString() } returns mockUUID
22 | every { SharedPreferenceUtils.instance.getString(any(), any(), any()) } returns null
23 | every { SharedPreferenceUtils.instance.putString(any(), any(), any()) } just runs
24 |
25 | val sut = UUIDHelper()
26 | val appGuid = sut.getInstallationGUID(mockk(relaxed = true))
27 | assertEquals(appGuid, mockUUID)
28 | }
29 |
30 | @Test
31 | fun `when appGUID has been set, uuidHelper returns it`() {
32 | val mockUUID = "mock-uuid"
33 | mockkObject(SharedPreferenceUtils)
34 | every { SharedPreferenceUtils.instance.getString(any(), any(), any()) } returns mockUUID
35 |
36 | val sut = UUIDHelper()
37 | val appGuid = sut.getInstallationGUID(mockk(relaxed = true))
38 | assertEquals(appGuid, mockUUID)
39 | }
40 |
41 | @After
42 | fun tearDown() {
43 | unmockkAll()
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/PayPalWebPayments/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/PayPalWebPayments/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | alias libs.plugins.android.library
3 | alias libs.plugins.kotlinAndroid
4 | }
5 |
6 | android {
7 | namespace 'com.paypal.android.paypalwebpayments'
8 | java {
9 | toolchain {
10 | languageVersion = modules.kotlinToolchainLanguageVersion
11 | }
12 | }
13 |
14 | defaultConfig {
15 | compileSdk modules.androidCompileSdk
16 | minSdkVersion modules.androidMinSdkVersion
17 | targetSdkVersion modules.androidTargetVersion
18 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
19 | consumerProguardFiles "consumer-rules.pro"
20 | }
21 |
22 | buildTypes {
23 | release {
24 | version = rootProject.version
25 | minifyEnabled false
26 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
27 | }
28 | }
29 |
30 | compileOptions {
31 | sourceCompatibility modules.sourceCompatibility
32 | targetCompatibility modules.targetCompatibility
33 | }
34 |
35 | kotlinOptions {
36 | jvmTarget = modules.kotlinJvmTarget
37 | }
38 |
39 | namespace 'com.paypal.android.paypalwebpayments'
40 | }
41 |
42 | dependencies {
43 | api project(':CorePayments')
44 | implementation libs.braintree.browserSwitch
45 |
46 | implementation libs.kotlin.stdLib
47 | implementation libs.androidx.coreKtx
48 | implementation libs.androidx.appcompat
49 | implementation libs.kotlinx.coroutinesAndroid
50 | implementation libs.android.material
51 |
52 | testImplementation libs.junit
53 | testImplementation libs.mockk
54 | testImplementation libs.robolectric
55 | testImplementation libs.kotlinx.coroutinesTest
56 | testImplementation libs.strikt.core
57 | testImplementation libs.strikt.mockk
58 |
59 | androidTestImplementation libs.androidx.test.junit
60 | androidTestImplementation libs.androidx.test.espresso
61 | }
62 |
63 | project.ext.name = "paypal-web-payments"
64 | project.ext.version = rootProject.version
65 | project.ext.pom_name = "PayPal SDK - Web Payments"
66 | project.ext.pom_desc = "Library for PayPal web payments"
67 |
68 | apply from: rootProject.file("gradle/gradle-publish.gradle")
--------------------------------------------------------------------------------
/PayPalWebPayments/consumer-rules.pro:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/paypal/paypal-android/9d77a9e760518e586a64c135ad7f225e4293fb20/PayPalWebPayments/consumer-rules.pro
--------------------------------------------------------------------------------
/PayPalWebPayments/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
--------------------------------------------------------------------------------
/PayPalWebPayments/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/PayPalWebPayments/src/main/java/com/paypal/android/paypalwebpayments/PayPalPresentAuthChallengeResult.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android.paypalwebpayments
2 |
3 | import com.paypal.android.corepayments.PayPalSDKError
4 |
5 | sealed class PayPalPresentAuthChallengeResult {
6 | data class Success(val authState: String) : PayPalPresentAuthChallengeResult()
7 | data class Failure(val error: PayPalSDKError) : PayPalPresentAuthChallengeResult()
8 | }
9 |
--------------------------------------------------------------------------------
/PayPalWebPayments/src/main/java/com/paypal/android/paypalwebpayments/PayPalWebCheckoutFinishStartResult.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android.paypalwebpayments
2 |
3 | import com.paypal.android.corepayments.PayPalSDKError
4 |
5 | sealed class PayPalWebCheckoutFinishStartResult {
6 | class Success(val orderId: String?, val payerId: String?) : PayPalWebCheckoutFinishStartResult()
7 | class Failure(val error: PayPalSDKError, val orderId: String?) : PayPalWebCheckoutFinishStartResult()
8 | class Canceled(val orderId: String?) : PayPalWebCheckoutFinishStartResult()
9 | data object NoResult : PayPalWebCheckoutFinishStartResult()
10 | }
11 |
--------------------------------------------------------------------------------
/PayPalWebPayments/src/main/java/com/paypal/android/paypalwebpayments/PayPalWebCheckoutFinishVaultResult.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android.paypalwebpayments
2 |
3 | import com.paypal.android.corepayments.PayPalSDKError
4 |
5 | sealed class PayPalWebCheckoutFinishVaultResult {
6 |
7 | class Success(val approvalSessionId: String) : PayPalWebCheckoutFinishVaultResult()
8 | class Failure(val error: PayPalSDKError) : PayPalWebCheckoutFinishVaultResult()
9 | data object Canceled : PayPalWebCheckoutFinishVaultResult()
10 | data object NoResult : PayPalWebCheckoutFinishVaultResult()
11 | }
12 |
--------------------------------------------------------------------------------
/PayPalWebPayments/src/main/java/com/paypal/android/paypalwebpayments/PayPalWebCheckoutFundingSource.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android.paypalwebpayments
2 |
3 | /**
4 | * Enum class to specify the type of funding for an order.
5 | * For more information go to: https://developer.paypal.com/docs/checkout/pay-later/us/
6 | */
7 | enum class PayPalWebCheckoutFundingSource(val value: String) {
8 | /**
9 | * PAYPAL_CREDIT will launch the web checkout flow and display PayPal Credit funding to eligible customers
10 | * Eligible costumers receive a revolving line of credit that they can use to pay over time.
11 | */
12 | PAYPAL_CREDIT("credit"),
13 |
14 | /**
15 | * PAY_LATER will launch the web checkout flow and display Pay Later offers to eligible customers,
16 | * which include short-term, interest-free payments and other special financing options.
17 | */
18 | PAY_LATER("paylater"),
19 |
20 | /**
21 | * PAYPAL will launch the web checkout for a one-time PayPal Checkout flow
22 | */
23 | PAYPAL("paypal")
24 | }
25 |
--------------------------------------------------------------------------------
/PayPalWebPayments/src/main/java/com/paypal/android/paypalwebpayments/PayPalWebCheckoutRequest.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android.paypalwebpayments
2 |
3 | /**
4 | * Creates an instance of a PayPalRequest.
5 | *
6 | * @param orderId The ID of the order to be approved.
7 | * @param fundingSource specify funding (credit, paylater or default)
8 | */
9 | data class PayPalWebCheckoutRequest @JvmOverloads constructor(
10 | val orderId: String,
11 | val fundingSource: PayPalWebCheckoutFundingSource = PayPalWebCheckoutFundingSource.PAYPAL
12 | )
13 |
--------------------------------------------------------------------------------
/PayPalWebPayments/src/main/java/com/paypal/android/paypalwebpayments/PayPalWebVaultRequest.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android.paypalwebpayments
2 |
3 | /**
4 | * Request to vault a PayPal payment method using [PayPalWebCheckoutClient.vault].
5 | *
6 | * @property [setupTokenId] ID for the setup token associated with the vault approval
7 | * @property [approveVaultHref] URL for the approval web page
8 | */
9 | data class PayPalWebVaultRequest @Deprecated("Use PayPalWebVaultRequest(setupTokenId) instead.")
10 | constructor(
11 | val setupTokenId: String,
12 | @Deprecated("The approveVaultHref property is no longer required and will be ignored.")
13 | val approveVaultHref: String? // NEXT_MAJOR_VERSION: - Remove this property
14 | ) {
15 |
16 | /**
17 | * Request to vault a PayPal payment method using [PayPalWebCheckoutClient.vault].
18 | *
19 | * @property [setupTokenId] ID for the setup token associated with the vault approval
20 | */
21 | constructor(setupTokenId: String) : this(setupTokenId, null)
22 | }
23 |
--------------------------------------------------------------------------------
/PayPalWebPayments/src/main/java/com/paypal/android/paypalwebpayments/analytics/CheckoutEvent.kt:
--------------------------------------------------------------------------------
1 | @file:Suppress("SpacingAroundParens", "NoMultipleSpaces", "MaxLineLength")
2 |
3 | package com.paypal.android.paypalwebpayments.analytics
4 |
5 | internal enum class CheckoutEvent(val value: String) {
6 | // @formatter:off
7 | STARTED( "paypal-web-payments:checkout:started"),
8 | SUCCEEDED("paypal-web-payments:checkout:succeeded"),
9 | FAILED( "paypal-web-payments:checkout:failed"),
10 | CANCELED( "paypal-web-payments:checkout:canceled"),
11 |
12 | AUTH_CHALLENGE_PRESENTATION_SUCCEEDED("paypal-web-payments:checkout:auth-challenge-presentation:succeeded"),
13 | AUTH_CHALLENGE_PRESENTATION_FAILED( "paypal-web-payments:checkout:auth-challenge-presentation:failed"),
14 | // @formatter:on
15 | }
16 |
--------------------------------------------------------------------------------
/PayPalWebPayments/src/main/java/com/paypal/android/paypalwebpayments/analytics/PayPalWebAnalytics.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android.paypalwebpayments.analytics
2 |
3 | import com.paypal.android.corepayments.analytics.AnalyticsService
4 |
5 | internal class PayPalWebAnalytics(private val analyticsService: AnalyticsService) {
6 |
7 | fun notify(event: CheckoutEvent, orderId: String?) {
8 | analyticsService.sendAnalyticsEvent(event.value, orderId)
9 | }
10 |
11 | fun notify(event: VaultEvent, setupTokenId: String?) {
12 | analyticsService.sendAnalyticsEvent(event.value, setupTokenId)
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/PayPalWebPayments/src/main/java/com/paypal/android/paypalwebpayments/analytics/VaultEvent.kt:
--------------------------------------------------------------------------------
1 | @file:Suppress("SpacingAroundParens", "NoMultipleSpaces", "MaxLineLength")
2 |
3 | package com.paypal.android.paypalwebpayments.analytics
4 |
5 | internal enum class VaultEvent(val value: String) {
6 | // @formatter:off
7 | STARTED( "paypal-web-payments:vault-wo-purchase:started"),
8 | SUCCEEDED("paypal-web-payments:vault-wo-purchase:succeeded"),
9 | FAILED( "paypal-web-payments:vault-wo-purchase:failed"),
10 | CANCELED( "paypal-web-payments:vault-wo-purchase:canceled"),
11 |
12 | AUTH_CHALLENGE_PRESENTATION_SUCCEEDED("paypal-web-payments:vault-wo-purchase:auth-challenge-presentation:succeeded"),
13 | AUTH_CHALLENGE_PRESENTATION_FAILED( "paypal-web-payments:vault-wo-purchase:auth-challenge-presentation:failed"),
14 | // @formatter:on
15 | }
16 |
--------------------------------------------------------------------------------
/PayPalWebPayments/src/main/java/com/paypal/android/paypalwebpayments/errors/PayPalWebCheckoutError.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android.paypalwebpayments.errors
2 |
3 | import com.paypal.android.corepayments.PayPalSDKError
4 |
5 | internal object PayPalWebCheckoutError {
6 |
7 | // 0. An unknown error occurred.
8 | val unknownError = PayPalSDKError(
9 | code = PayPalWebCheckoutErrorCode.UNKNOWN.ordinal,
10 | errorDescription = "An unknown error occurred. Contact developer.paypal.com/support."
11 | )
12 |
13 | // 1. Result did not contain the expected data.
14 | val malformedResultError = PayPalSDKError(
15 | code = PayPalWebCheckoutErrorCode.MALFORMED_RESULT.ordinal,
16 | errorDescription = "Result did not contain the expected data. Payer ID or Order ID is null."
17 | )
18 |
19 | // 2. An error occurred while browser switching
20 | fun browserSwitchError(cause: Exception) = PayPalSDKError(
21 | code = PayPalWebCheckoutErrorCode.BROWSER_SWITCH.ordinal,
22 | errorDescription = cause.message ?: "Unable to Browser Switch"
23 | )
24 | }
25 |
--------------------------------------------------------------------------------
/PayPalWebPayments/src/main/java/com/paypal/android/paypalwebpayments/errors/PayPalWebCheckoutErrorCode.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android.paypalwebpayments.errors
2 |
3 | internal enum class PayPalWebCheckoutErrorCode {
4 | UNKNOWN,
5 | MALFORMED_RESULT,
6 | BROWSER_SWITCH
7 | }
8 |
--------------------------------------------------------------------------------
/PayPalWebPayments/src/test/java/com/paypal/android/paypalwebpayments/PayPalWebCheckoutRequestUnitTest.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android.paypalwebpayments
2 |
3 | import org.junit.Assert.assertEquals
4 | import org.junit.Test
5 |
6 | class PayPalWebCheckoutRequestUnitTest {
7 |
8 | @Test
9 | fun `given an order id, PayPalRequest should return the same orderId`() {
10 | val orderId = "fake_order_id"
11 | val payPalRequest = PayPalWebCheckoutRequest(orderId)
12 | assertEquals(orderId, payPalRequest.orderId)
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/PayPalWebPayments/src/test/resources/robolectric.properties:
--------------------------------------------------------------------------------
1 | # TODO: remove this file when Robolectric supports API level 35 (Android 15)
2 | sdk=33
3 |
--------------------------------------------------------------------------------
/PaymentButtons/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | alias libs.plugins.android.library
3 | alias libs.plugins.kotlinAndroid
4 | }
5 |
6 | android {
7 | namespace 'com.paypal.android.ui'
8 | java {
9 | toolchain {
10 | languageVersion = modules.kotlinToolchainLanguageVersion
11 | }
12 | }
13 |
14 | defaultConfig {
15 | compileSdk modules.androidCompileSdk
16 | minSdkVersion modules.androidMinSdkVersion
17 | targetSdkVersion modules.androidTargetVersion
18 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
19 | consumerProguardFiles "consumer-rules.pro"
20 | }
21 |
22 | buildTypes {
23 | release {
24 | version = rootProject.version
25 | minifyEnabled false
26 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
27 | }
28 | }
29 |
30 | compileOptions {
31 | sourceCompatibility modules.sourceCompatibility
32 | targetCompatibility modules.targetCompatibility
33 | }
34 |
35 | kotlinOptions {
36 | jvmTarget = modules.kotlinJvmTarget
37 | }
38 |
39 | namespace 'com.paypal.android.ui'
40 | }
41 |
42 | dependencies {
43 | implementation libs.androidx.coreKtx
44 | implementation libs.android.material
45 |
46 | implementation project(':CorePayments')
47 | }
48 |
49 | project.ext.name = "payment-buttons"
50 | project.ext.version = rootProject.version
51 | project.ext.pom_name = "PayPal SDK - Payment Buttons"
52 | project.ext.pom_desc = "Library for PayPal's payment buttons and UI components"
53 |
54 | apply from: rootProject.file("gradle/gradle-publish.gradle")
--------------------------------------------------------------------------------
/PaymentButtons/consumer-rules.pro:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/paypal/paypal-android/9d77a9e760518e586a64c135ad7f225e4293fb20/PaymentButtons/consumer-rules.pro
--------------------------------------------------------------------------------
/PaymentButtons/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
--------------------------------------------------------------------------------
/PaymentButtons/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/PaymentButtons/src/main/java/com/paypal/android/paymentbuttons/PaymentButtonColor.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android.paymentbuttons
2 |
3 | import android.content.Context
4 | import android.content.res.ColorStateList
5 | import androidx.core.content.ContextCompat
6 |
7 | /**
8 | * PaymentButtonColor provides a structure for all colors that can be used within a [PaymentButton].
9 | *
10 | * @property colorResId is the color resource ID that can be used for displaying the color.
11 | * @property hasOutline when true the color should be displayed with an outline, when false then
12 | * no outline will be drawn.
13 | * @property luminance defines the luminance of the color, useful when determining which type of
14 | * wordmark to use or when determining the surface color of text widgets on the button.
15 | */
16 | interface PaymentButtonColor {
17 | val colorResId: Int
18 |
19 | val hasOutline: Boolean
20 |
21 | val luminance: PaymentButtonColorLuminance
22 |
23 | /**
24 | * Provides the correct [ColorStateList] given a [Context].
25 | *
26 | * @return [ColorStateList] which corresponds to the [PayPalButtonColor].
27 | */
28 | fun retrieveColorResource(context: Context): ColorStateList {
29 | return ContextCompat.getColorStateList(context, colorResId)!!
30 | }
31 | }
32 |
33 | /**
34 | * ColorLuminance defines the intensity of the light emitted by a given color with simplified
35 | * [DARK] and [LIGHT] values.
36 | *
37 | * This is helpful when modifying internal button styles (wordmark and text for example) to
38 | * change those colors based on the button's background color while still allow for exhaustive
39 | * compile time checks (something not currently possible with booleans).
40 | */
41 | enum class PaymentButtonColorLuminance {
42 | DARK, LIGHT;
43 | }
44 |
--------------------------------------------------------------------------------
/PaymentButtons/src/main/java/com/paypal/android/paymentbuttons/PaymentButtonShape.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android.paymentbuttons
2 |
3 | import com.paypal.android.paymentbuttons.error.createFormattedIllegalArgumentException
4 |
5 | /**
6 | * Defines the shapes available for payment buttons. If no shape is provided then the default
7 | * button style will be retrieved from the applications root style.
8 | *
9 | * @see ROUNDED will render the button with rounded corners.
10 | * @see PILL will render the button with circular corners on either side, it looks like a pill.
11 | * @see RECTANGLE will render the button with sharp corners, it will look like a rectangle.
12 | */
13 | enum class PaymentButtonShape(val value: Int) {
14 | ROUNDED(value = 0),
15 | PILL(value = 1),
16 | RECTANGLE(value = 2);
17 |
18 | companion object {
19 | /**
20 | * Given an [attributeIndex] this will provide the correct [PaymentButtonShape]. If an
21 | * invalid [attributeIndex] is provided then it will throw an [IllegalArgumentException].
22 | *
23 | * @throws [IllegalArgumentException] when an invalid index is provided.
24 | */
25 | operator fun invoke(attributeIndex: Int): PaymentButtonShape {
26 | return when (attributeIndex) {
27 | ROUNDED.value -> ROUNDED
28 | PILL.value -> PILL
29 | RECTANGLE.value -> RECTANGLE
30 | else -> throw createFormattedIllegalArgumentException("PaymentButtonShape", values().size)
31 | }
32 | }
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/PaymentButtons/src/main/java/com/paypal/android/paymentbuttons/error/Exceptions.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android.paymentbuttons.error
2 |
3 | internal fun createFormattedIllegalArgumentException(enumName: String, enumValues: Int): IllegalArgumentException {
4 | val exceptionMessage = "Attempted to create a $enumName with an invalid index. Please use an" +
5 | " index that is between 0 and ${enumValues - 1} and try again."
6 | return IllegalArgumentException(exceptionMessage)
7 | }
8 |
--------------------------------------------------------------------------------
/PaymentButtons/src/main/res/color/paypal_black.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/PaymentButtons/src/main/res/color/paypal_blue.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/PaymentButtons/src/main/res/color/paypal_dark_blue.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/PaymentButtons/src/main/res/color/paypal_gold.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/PaymentButtons/src/main/res/color/paypal_silver.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/PaymentButtons/src/main/res/color/paypal_white.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/PaymentButtons/src/main/res/drawable/logo_paypal_color.xml:
--------------------------------------------------------------------------------
1 |
6 |
12 |
18 |
24 |
25 |
--------------------------------------------------------------------------------
/PaymentButtons/src/main/res/drawable/logo_paypal_monochrome.xml:
--------------------------------------------------------------------------------
1 |
6 |
12 |
18 |
24 |
25 |
--------------------------------------------------------------------------------
/PaymentButtons/src/main/res/font/paypalopen_regular.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/paypal/paypal-android/9d77a9e760518e586a64c135ad7f225e4293fb20/PaymentButtons/src/main/res/font/paypalopen_regular.otf
--------------------------------------------------------------------------------
/PaymentButtons/src/main/res/layout/paypal_ui_payment_button_view.xml:
--------------------------------------------------------------------------------
1 |
2 |
11 |
12 |
22 |
23 |
31 |
32 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/PaymentButtons/src/main/res/values/attrs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
--------------------------------------------------------------------------------
/PaymentButtons/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #8f000000
4 |
5 | #000000
6 | #FFFFFF
7 | #000000
8 |
--------------------------------------------------------------------------------
/PaymentButtons/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 8dp
4 |
5 |
6 | 4dp
7 | 0dp
8 | 24dp
9 | @dimen/margin_8x
10 |
11 | 1dp
12 |
13 | 48dp
14 | 55dp
15 |
17 | 13dp
18 | 15dp
19 | 14sp
20 | 18sp
21 | 16dp
22 |
23 |
--------------------------------------------------------------------------------
/PaymentButtons/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | PayPal Button
5 | PayPal Credit Button
6 | Buy Now
7 | Checkout
8 | Pay with
9 | Pay Later
10 |
--------------------------------------------------------------------------------
/Venmo/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | alias libs.plugins.android.library
3 | alias libs.plugins.kotlinAndroid
4 | }
5 |
6 | android {
7 | namespace 'com.paypal.android.venmo'
8 | java {
9 | toolchain {
10 | languageVersion = modules.kotlinToolchainLanguageVersion
11 | }
12 | }
13 |
14 | defaultConfig {
15 | compileSdk modules.androidCompileSdk
16 | minSdkVersion modules.androidMinSdkVersion
17 | targetSdkVersion modules.androidTargetVersion
18 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
19 | consumerProguardFiles "consumer-rules.pro"
20 | }
21 |
22 | buildTypes {
23 | release {
24 | version = rootProject.version
25 | minifyEnabled false
26 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
27 | }
28 | }
29 |
30 | compileOptions {
31 | sourceCompatibility modules.sourceCompatibility
32 | targetCompatibility modules.targetCompatibility
33 | }
34 |
35 | kotlinOptions {
36 | jvmTarget = modules.kotlinJvmTarget
37 | }
38 | namespace 'com.paypal.android.venmo'
39 | }
40 |
41 | dependencies {
42 | implementation libs.androidx.coreKtx
43 | implementation libs.androidx.appcompat
44 | implementation libs.android.material
45 |
46 | testImplementation libs.junit
47 |
48 | androidTestImplementation libs.androidx.test.junit
49 | androidTestImplementation libs.androidx.test.espresso
50 | }
51 |
--------------------------------------------------------------------------------
/Venmo/consumer-rules.pro:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/paypal/paypal-android/9d77a9e760518e586a64c135ad7f225e4293fb20/Venmo/consumer-rules.pro
--------------------------------------------------------------------------------
/Venmo/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
--------------------------------------------------------------------------------
/Venmo/src/androidTest/java/com/paypal/android/venmo/ExampleInstrumentedTest.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android.venmo
2 |
3 | import androidx.test.ext.junit.runners.AndroidJUnit4
4 | import androidx.test.platform.app.InstrumentationRegistry
5 | import org.junit.Assert.assertEquals
6 | import org.junit.Test
7 | import org.junit.runner.RunWith
8 |
9 | /**
10 | * Instrumented test, which will execute on an Android device.
11 | *
12 | * See [testing documentation](http://d.android.com/tools/testing).
13 | */
14 | @RunWith(AndroidJUnit4::class)
15 | class ExampleInstrumentedTest {
16 | @Test
17 | fun useAppContext() {
18 | // Context of the app under test.
19 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext
20 | assertEquals("com.paypal.android.venmo.test", appContext.packageName)
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/Venmo/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/Venmo/src/test/java/com/paypal/android/venmo/ExampleUnitTest.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.android.venmo
2 |
3 | import org.junit.Assert.assertEquals
4 | import org.junit.Test
5 |
6 | /**
7 | * Example local unit test, which will execute on the development machine (host).
8 | *
9 | * See [testing documentation](http://d.android.com/tools/testing).
10 | */
11 | class ExampleUnitTest {
12 | @Test
13 | fun addition_isCorrect() {
14 | assertEquals(4, 2 + 2)
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/adr/1-remove-graphql-generics/figure-graph-ql-client.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/paypal/paypal-android/9d77a9e760518e586a64c135ad7f225e4293fb20/adr/1-remove-graphql-generics/figure-graph-ql-client.png
--------------------------------------------------------------------------------
/adr/1-remove-graphql-generics/figure-payments-sdk-architecture.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/paypal/paypal-android/9d77a9e760518e586a64c135ad7f225e4293fb20/adr/1-remove-graphql-generics/figure-payments-sdk-architecture.png
--------------------------------------------------------------------------------
/adr/1-remove-graphql-generics/figure-query-abstract-base-class.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/paypal/paypal-android/9d77a9e760518e586a64c135ad7f225e4293fb20/adr/1-remove-graphql-generics/figure-query-abstract-base-class.png
--------------------------------------------------------------------------------
/adr/2-remove-core-api-and-http-request-factories/figure-card-client-example.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/paypal/paypal-android/9d77a9e760518e586a64c135ad7f225e4293fb20/adr/2-remove-core-api-and-http-request-factories/figure-card-client-example.png
--------------------------------------------------------------------------------
/adr/2-remove-core-api-and-http-request-factories/figure-deep-module-vs-shallow-module.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/paypal/paypal-android/9d77a9e760518e586a64c135ad7f225e4293fb20/adr/2-remove-core-api-and-http-request-factories/figure-deep-module-vs-shallow-module.png
--------------------------------------------------------------------------------
/adr/2-remove-core-api-and-http-request-factories/figure-multi-api-uml.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/paypal/paypal-android/9d77a9e760518e586a64c135ad7f225e4293fb20/adr/2-remove-core-api-and-http-request-factories/figure-multi-api-uml.png
--------------------------------------------------------------------------------
/codecov.yml:
--------------------------------------------------------------------------------
1 | codecov:
2 | require_ci_to_pass: yes
3 |
4 | coverage:
5 | status:
6 | project: off
7 | patch: off
8 |
9 | parsers:
10 | gcov:
11 | branch_detection:
12 | conditional: yes
13 | loop: yes
14 | method: no
15 | macro: no
16 |
17 | comment:
18 | layout: "reach,diff,flags,files,footer"
19 | behavior: default
20 | require_changes: no
21 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 | # Specifies the JVM arguments used for the daemon process.
8 | # The setting is particularly useful for tweaking memory settings.
9 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
10 | # When configured, Gradle will run in incubating parallel mode.
11 | # This option should only be used with decoupled projects. More details, visit
12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
13 | # org.gradle.parallel=true
14 | # AndroidX package structure to make it clearer which packages are bundled with the
15 | # Android operating system, and which are packaged with your app"s APK
16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn
17 | android.useAndroidX=true
18 | # Kotlin code style for this project: "official" or "obsolete":
19 | kotlin.code.style=official
20 |
21 | POM_PACKAGING=aar
22 | #TODO: needs to be defined
23 | POM_URL=not_defined
24 |
25 | POM_SCM_URL=https://github.com/paypal/paypal-android
26 | POM_SCM_CONNECTION=scm:git@github.com:paypal/paypal-android.git
27 | POM_SCM_DEV_CONNECTION=scm:git@github.com:paypal/paypal-android.git
28 |
29 | POM_LICENCE_NAME=The Apache License, Version 2.0
30 | POM_LICENCE_URL=http://www.apache.org/licenses/LICENSE-2.0
31 |
32 | POM_DEVELOPER_ID=paypal-android
33 | POM_DEVELOPER_NAME=PayPal Android
34 | #TODO: needs to be defined
35 | POM_DEVELOPER_EMAIL=not_defined
36 |
37 | android.defaults.buildfeatures.buildconfig=true
38 | android.nonTransitiveRClass=false
39 | android.nonFinalResIds=false
40 |
41 |
--------------------------------------------------------------------------------
/gradle/gradle-publish.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'maven-publish'
2 | apply plugin: 'signing'
3 |
4 | ext["signing.keyId"] = System.getenv('SIGNING_KEY_ID') ?: ''
5 | ext["signing.password"] = System.getenv('SIGNING_KEY_PASSWORD') ?: ''
6 | ext["signing.secretKeyRingFile"] = System.getenv('SIGNING_KEY_FILE') ?: ''
7 |
8 | android {
9 | publishing {
10 | singleVariant('release') {
11 | withSourcesJar()
12 | }
13 | }
14 | }
15 |
16 | publishing {
17 | publications {
18 | release(MavenPublication) {
19 | groupId group
20 | version project.ext.version
21 | artifactId project.ext.name
22 |
23 | afterEvaluate {
24 | from components.release
25 | }
26 |
27 | pom {
28 | name = project.ext.pom_name ?: ''
29 | packaging = POM_PACKAGING
30 | description = project.ext.pom_desc ?: ''
31 | url = POM_URL
32 | licenses {
33 | license {
34 | name = POM_LICENCE_NAME
35 | url = POM_LICENCE_URL
36 | }
37 | }
38 | developers {
39 | developer {
40 | id = POM_DEVELOPER_ID
41 | name = POM_DEVELOPER_NAME
42 | email = POM_DEVELOPER_EMAIL
43 | }
44 | }
45 | scm {
46 | connection = POM_SCM_CONNECTION
47 | developerConnection = POM_SCM_DEV_CONNECTION
48 | url = POM_SCM_URL
49 | }
50 | }
51 | }
52 | }
53 |
54 | signing {
55 | sign publishing.publications.release
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/paypal/paypal-android/9d77a9e760518e586a64c135ad7f225e4293fb20/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Wed Jun 21 09:49:22 CDT 2023
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip
5 | zipStoreBase=GRADLE_USER_HOME
6 | zipStorePath=wrapper/dists
7 |
--------------------------------------------------------------------------------
/lint.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | // TODO: Remove this ignore when SDK has been tested against API 31
6 |
7 | // TODO: Remove this once Self Service Android Studio Version has been updated
8 |
9 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | pluginManagement {
2 | repositories {
3 | google {
4 | content {
5 | includeGroupByRegex("com\\.android.*")
6 | includeGroupByRegex("com\\.google.*")
7 | includeGroupByRegex("androidx.*")
8 | }
9 | }
10 | mavenCentral()
11 | gradlePluginPortal()
12 | }
13 | }
14 |
15 | dependencyResolutionManagement {
16 | repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
17 | repositories {
18 | google()
19 | mavenCentral()
20 | mavenLocal()
21 | maven {
22 | url 'https://oss.sonatype.org/content/repositories/snapshots/'
23 | }
24 | }
25 | }
26 |
27 | rootProject.name = "AndroidSDK"
28 | include ':CardPayments'
29 | include ':CorePayments'
30 | include ':Demo'
31 | include ':FraudProtection'
32 | include ':PayPalWebPayments'
33 | include ':PaymentButtons'
34 | include ':Venmo'
35 |
--------------------------------------------------------------------------------