├── .github
├── ISSUE_TEMPLATE
│ ├── bug_report.md
│ ├── feature_request.md
│ └── other.md
└── workflows
│ └── auto-reply-to-raised-issues.yml
├── .gitignore
├── .idea
├── .name
└── codeStyles
│ ├── Project.xml
│ └── codeStyleConfig.xml
├── CHANGELOG.md
├── LICENSE.txt
├── README.md
├── build.gradle
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── quickstart-kotlin
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── com
│ │ └── paypal
│ │ └── checkoutsamples
│ │ ├── KotlinQuickStartActivity.kt
│ │ ├── QuickStartApp.kt
│ │ ├── QuickStartConstants.kt
│ │ ├── order
│ │ ├── CreateItemDialog.kt
│ │ ├── OrdersQuickStartActivity.kt
│ │ └── usecase
│ │ │ ├── CreateAmountUseCase.kt
│ │ │ ├── CreateItemsUseCase.kt
│ │ │ ├── CreateOrderUseCase.kt
│ │ │ ├── CreatePurchaseUnitUseCase.kt
│ │ │ └── CreateShippingUseCase.kt
│ │ ├── paymentbutton
│ │ └── PaymentButtonQuickStartActivity.kt
│ │ └── token
│ │ ├── TokenQuickStartActivity.kt
│ │ └── repository
│ │ ├── AuthTokenRepository.kt
│ │ ├── CheckoutApi.kt
│ │ ├── OrderRepository.kt
│ │ ├── request
│ │ └── OrderRequest.kt
│ │ └── response
│ │ ├── OAuthTokenResponse.kt
│ │ └── OrderResponse.kt
│ └── res
│ ├── drawable-v24
│ └── ic_launcher_foreground.xml
│ ├── drawable
│ └── ic_launcher_background.xml
│ ├── layout
│ ├── activity_kotlin_quick_start.xml
│ ├── activity_orders_quick_start.xml
│ ├── activity_payment_button_quick_start.xml
│ ├── activity_token_quick_start.xml
│ ├── dialog_create_item.xml
│ └── item_preview_item.xml
│ ├── mipmap-anydpi-v26
│ ├── ic_launcher.xml
│ └── ic_launcher_round.xml
│ ├── mipmap-hdpi
│ ├── ic_launcher.png
│ └── ic_launcher_round.png
│ ├── mipmap-mdpi
│ ├── ic_launcher.png
│ └── ic_launcher_round.png
│ ├── mipmap-xhdpi
│ ├── ic_launcher.png
│ └── ic_launcher_round.png
│ ├── mipmap-xxhdpi
│ ├── ic_launcher.png
│ └── ic_launcher_round.png
│ ├── mipmap-xxxhdpi
│ ├── ic_launcher.png
│ └── ic_launcher_round.png
│ └── values
│ ├── colors.xml
│ ├── dimens.xml
│ ├── strings.xml
│ └── styles.xml
└── settings.gradle
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Report a bug for the Checkout Android SDK (https://developer.paypal.com/docs/business/native-checkout/android).
4 | title: ''
5 | labels: "\U0001F41E bug"
6 | assignees: ''
7 |
8 | ---
9 |
10 | Before you create a new issue, please search for similar issues. It's possible somebody has encountered this bug already. **PLEASE REMOVE THIS LINE TO ACKNOWLEDGE THAT AN ISSUE DOESN'T ALREADY EXIST FOR THIS BUG**
11 |
12 | **Describe the bug**
13 | A clear and concise description of what the bug is.
14 |
15 | **To Reproduce**
16 | Steps to reproduce the behavior:
17 | 1. Go to '...'
18 | 2. Click on '....'
19 | 3. Scroll down to '....'
20 | 4. See error
21 |
22 | **Expected behavior**
23 | A clear and concise description of what you expected to happen.
24 |
25 | **Screenshots**
26 | If applicable, add screenshots to help explain your problem.
27 |
28 | **Smartphone (please complete the following information):**
29 | - Android Version: [e.g. API 30]
30 | - Device: [e.g. Emulator or One Plus 9]
31 | - Browser [e.g. chrome, safari] <- only applicable for app switches or fallback situations
32 | - SDK Version [e.g. 0.1.0]
33 | - Package name of your app [e.g. com.example.my_app]
34 | - Client ID
35 |
36 | **Additional context**
37 | Add any other context about the problem here.
38 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for the Checkout Android SDK (https://developer.paypal.com/docs/business/native-checkout/android).
4 | title: ''
5 | labels: "\U0001F9DE♂️ feature"
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Is your feature request related to a problem? Please describe.**
11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
12 |
13 | **Describe the solution you'd like**
14 | A clear and concise description of what you want to happen.
15 |
16 | **Describe alternatives you've considered**
17 | A clear and concise description of any alternative solutions or features you've considered.
18 |
19 | **Additional context**
20 | Add any other context or screenshots about the feature request here.
21 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/other.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Other
3 | about: If the issue is not a bug or a feature request then please use this option.
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 |
11 |
--------------------------------------------------------------------------------
/.github/workflows/auto-reply-to-raised-issues.yml:
--------------------------------------------------------------------------------
1 | # from https://docs.github.com/en/actions/managing-issues-and-pull-requests/commenting-on-an-issue-when-a-label-is-added
2 |
3 | # This workflow uses actions that are not certified by GitHub.
4 | # They are provided by a third-party and are governed by
5 | # separate terms of service, privacy policy, and support
6 | # documentation.
7 |
8 | # GitHub recommends pinning actions to a commit SHA.
9 | # To get a newer version, you will need to update the SHA.
10 | # You can also reference a tag or branch, but the action may change without warning.
11 |
12 | name: Auto response to raised live issues
13 | on:
14 | issues:
15 | types: [opened]
16 |
17 | jobs:
18 | add-comment:
19 | runs-on: ubuntu-latest
20 | permissions:
21 | issues: write
22 | steps:
23 | - name: Add automated comment
24 | uses: peter-evans/create-or-update-comment@v3
25 | with:
26 | issue-number: ${{ github.event.issue.number }}
27 | body: |
28 | Thank you for reaching out to the Native Checkout SDK team. This integration path is now inactive for new merchants.
29 | If you are an existing merchant, please contact us [here](https://www.paypal.com/us/business/contact-sales) for further assistance.
30 |
31 | New merchants can integrate the Native Checkout experience via the Braintree Android SDK or PayPal Android SDK.
32 | For more information please see their respective developer documentation linked below.
33 | * [Braintree Android SDK](https://developer.paypal.com/braintree/docs/guides/overview)
34 | * [PayPal Android SDK](https://developer.paypal.com/beta/advanced-mobile/integrate-android/)
35 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Built application files
2 | *.apk
3 | *.ap_
4 | *.aab
5 |
6 | # Files for the ART/Dalvik VM
7 | *.dex
8 |
9 | # Java class files
10 | *.class
11 |
12 | # Generated files
13 | bin/
14 | gen/
15 | out/
16 | release/
17 |
18 | # Gradle files
19 | .gradle/
20 | build/
21 |
22 | # Local configuration file (sdk path, etc)
23 | local.properties
24 |
25 | # Proguard folder generated by Eclipse
26 | proguard/
27 |
28 | # Log Files
29 | *.log
30 |
31 | # Android Studio Navigation editor temp files
32 | .navigation/
33 |
34 | # Android Studio captures folder
35 | captures/
36 |
37 | # IntelliJ
38 | *.iml
39 | .idea/workspace.xml
40 | .idea/tasks.xml
41 | .idea/gradle.xml
42 | .idea/assetWizardSettings.xml
43 | .idea/dictionaries
44 | .idea/libraries
45 | .idea/misc.xml
46 | .idea/jarRepositories.xml
47 | # Android Studio 3 in .gitignore file.
48 | .idea/caches
49 | .idea/modules.xml
50 | .idea/navEditor.xml
51 | .idea/compiler.xml
52 |
53 | # Keystore files
54 | *.jks
55 | *.keystore
56 |
57 | # External native build folder generated in Android Studio 2.2 and later
58 | .externalNativeBuild
59 |
60 | # Google Services (e.g. APIs or Firebase)
61 | # google-services.json
62 |
63 | # Freeline
64 | freeline.py
65 | freeline/
66 | freeline_project_description.json
67 |
68 | # fastlane
69 | fastlane/report.xml
70 | fastlane/Preview.html
71 | fastlane/screenshots
72 | fastlane/test_output
73 | fastlane/readme.md
74 |
75 | # Version control
76 | vcs.xml
77 |
78 | # lint
79 | lint/intermediates/
80 | lint/generated/
81 | lint/outputs/
82 | lint/tmp/
83 | lint/reports/
84 |
85 | # macOS
86 | .DS_Store
87 |
--------------------------------------------------------------------------------
/.idea/.name:
--------------------------------------------------------------------------------
1 | Android Native Checkout Samples
--------------------------------------------------------------------------------
/.idea/codeStyles/Project.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 | xmlns:android
29 |
30 | ^$
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 | xmlns:.*
40 |
41 | ^$
42 |
43 |
44 | BY_NAME
45 |
46 |
47 |
48 |
49 |
50 |
51 | .*:id
52 |
53 | http://schemas.android.com/apk/res/android
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 | .*:name
63 |
64 | http://schemas.android.com/apk/res/android
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 | name
74 |
75 | ^$
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 | style
85 |
86 | ^$
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 | .*
96 |
97 | ^$
98 |
99 |
100 | BY_NAME
101 |
102 |
103 |
104 |
105 |
106 |
107 | .*
108 |
109 | http://schemas.android.com/apk/res/android
110 |
111 |
112 | ANDROID_ATTRIBUTE_ORDER
113 |
114 |
115 |
116 |
117 |
118 |
119 | .*
120 |
121 | .*
122 |
123 |
124 | BY_NAME
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
--------------------------------------------------------------------------------
/.idea/codeStyles/codeStyleConfig.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Change Log
2 |
3 | ## Version 1.3.2
4 |
5 | * Upgraded the Magnes SDK to version 5.5.1
6 |
7 | ## Version 1.3.1
8 |
9 | * Upgraded the Cardinal SDK to version 2.2.7-5
10 |
11 | ## Version 1.3.0
12 |
13 | * Add `hasUserLocationConsent` parameter to `PayPalCheckout.start()`, `PayPalCheckout.startCheckout()`, `PaymentButtonContainer.setup()`, and `PaymentButton.setup()`
14 |
15 | Note: Merchant applications are responsible for collecting user data consent. If your app has obtained consent from the user to collect location data in compliance with
16 | [Google Play Developer Program policies](https://support.google.com/googleplay/android-developer/answer/10144311#personal-sensitive),
17 | set `hasUserLocationConsent` to `true`. This flag enables PayPal to collect necessary information required for Fraud Detection and Risk Management.
18 |
19 | Merchant applications may be required to display a disclosure before collecting user location data in accordance with Google’s
20 | [Best practices for prominent disclosures and consent](https://support.google.com/googleplay/android-developer/answer/11150561?hl=en&ref_topic=12797379&sjid=10421482417907285178-NC).
21 | By setting `userHasLocationConsent` to true, your app is enabled to share device location data with a third party (PayPal) for Fraud Detection and Risk Management.
22 |
23 | [Examples of prominent in-app disclosures](https://support.google.com/googleplay/android-developer/answer/9799150?hl=en#Prominent%20in-app%20disclosure)
24 |
25 | Note: If you are seeing the Play Store flag your APK after updating to this version, please try following these steps:
26 | 1. Go to your Play Console
27 | 2. Select the app
28 | 3. Go to App bundle explorer
29 | 4. Select the violating APK/app bundle's App version at the top right dropdown menu, and make a note of which releases they are under
30 | 5. Go to the track with the violation. It will be one of these 4 pages: Internal / Closed / Open testing or Production
31 | 6. Near the top right of the page, click Create new release. (You may need to click Manage track first)
32 | If the release with the violating APK is in a draft state, discard the release
33 | 7. Add the new version of app bundles or APKs
34 | Make sure the non-compliant version of app bundles or APKs is under the Not included section of this release
35 | 8. To save any changes you make to your release, select Save
36 | 9. When you've finished preparing your release, select Review release, and then proceed to roll out the release to 100%.
37 | 10. If the violating APK is released to multiple tracks, repeat steps 5-9 in each track
38 |
39 | ## Version 1.2.1
40 |
41 | * Upgraded the data-collector SDK to version 3.21.0 which made updates to Device Data collection related to Google Play's User Data Policy
42 |
43 | Note: If you are seeing the Play Store flag your APK after updating to this version, please try following these steps:
44 | 1. Go to your Play Console
45 | 2. Select the app
46 | 3. Go to App bundle explorer
47 | 4. Select the violating APK/app bundle's App version at the top right dropdown menu, and make a note of which releases they are under
48 | 5. Go to the track with the violation. It will be one of these 4 pages: Internal / Closed / Open testing or Production
49 | 6. Near the top right of the page, click Create new release. (You may need to click Manage track first)
50 | If the release with the violating APK is in a draft state, discard the release
51 | 7. Add the new version of app bundles or APKs
52 | Make sure the non-compliant version of app bundles or APKs is under the Not included section of this release
53 | 8. To save any changes you make to your release, select Save
54 | 9. When you've finished preparing your release, select Review release, and then proceed to roll out the release to 100%.
55 | 10. If the violating APK is released to multiple tracks, repeat steps 5-9 in each track
56 |
57 | ## Version 1.2.0
58 |
59 | **This build enables the native flows for buyers in Australia. All other buyers, except those in US, Canada, EU, UK and Australia, will experience web fallbacks.**
60 |
61 | The following changes are included in this release:
62 |
63 | * Minor UI fixes
64 | * Internal improvements.
65 |
66 | ## Version 1.1.0
67 |
68 | **This build enables the native flows for buyers in the US, Canada, EU, and UK. All other buyers will see web fallback experiences.**
69 |
70 | The following changes are included in this release:
71 |
72 | * Buy Now Pay Later entry points
73 | * Improved authentication flows in SCA regions
74 | * Minor UI fixes
75 |
76 | ## Version 1.0.0
77 |
78 | **This build enables the native flows for buyers in the US and Canada. EU buyers will see a web fallback experience.**
79 |
80 | The following changes are included in this release:
81 |
82 | * Fixed UI bugs to ensure a consistent experience for multiple countries and languages
83 | * Fixed an issue that caused users to continue checkout on web when an invalid currency was selected
84 | * Ensures return of relevant billing information such as First Name, Last Name, and Email in the onApprove callback
85 | * Added resource name qualifier to avoid name collisions
86 | * Deprecated the following order actions
87 | * Patch
88 | * Capture
89 | * Authorize
90 | * GetDetails
91 | * ExecuteBillingAgreement
92 | * onShippingChangeAction.Patch
93 |
94 |
95 | ## Version 0.112.2
96 |
97 | * Added a Discount field to the Cart Details UI on the checkout home page
98 | * Addressed some issues on Vault flows
99 | * Minor UI fixes
100 | * Change policies link to point to user agreement screen
101 | * Third party providers compliance work
102 |
103 | ## Version 0.112.0
104 | **Features Updates**
105 | * Introduced Pay Later PayPal offerings
106 | * Added the ability for users to add an initial payment method to their PayPal account
107 | * Updated the UI of the native add-card flow to include complete address input
108 | * Added the ability to programmatically logout a PayPal user
109 |
110 | **Breaking Changes**
111 | * `Order` was renamed to `OrderRequest`
112 | * `SettingsConfig.shouldFailEligibility` was renamed to `SettingsConfig.showWebCheckout`
113 | * `Address` returned in the `OnShippingChange` callback was renamed to `ShippingChangeAddress`
114 |
115 | **Non-Breaking Changes**
116 | * Updated version to 0.112.0 to align with iOS
117 | * Additional bugfixes
118 |
119 | **GitHub Issues Resolved**
120 | https://github.com/paypal/android-checkout-sdk/issues/99
121 | https://github.com/paypal/android-checkout-sdk/issues/100
122 | https://github.com/paypal/android-checkout-sdk/issues/104
123 | https://github.com/paypal/android-checkout-sdk/issues/226
124 | https://github.com/paypal/android-checkout-sdk/issues/184
125 | https://github.com/paypal/android-checkout-sdk/issues/216
126 |
127 | ## Version 0.8.8
128 | * Adding the `ReturnUrl` requirement back into the `CheckoutConfig`
129 |
130 | ## Version 0.8.7
131 | * Upgraded the Cardinal SDK to version 2.2.7-2 which made updates to Device Data collection related to Google Play's User Data Policy
132 |
133 | Note: If you are seeing the Play Store flag your APK after updating to this version, please try following these steps:
134 | 1. Go to your Play Console
135 | 2. Select the app
136 | 3. Go to App bundle explorer
137 | 4. Select the violating APK/app bundle's App version at the top right dropdown menu, and make a note of which releases they are under
138 | 5. Go to the track with the violation. It will be one of these 4 pages: Internal / Closed / Open testing or Production
139 | 6. Near the top right of the page, click Create new release. (You may need to click Manage track first)
140 | If the release with the violating APK is in a draft state, discard the release
141 | 7. Add the new version of app bundles or APKs
142 | Make sure the non-compliant version of app bundles or APKs is under the Not included section of this release
143 | 8. To save any changes you make to your release, select Save
144 | 9. When you've finished preparing your release, select Review release, and then proceed to roll out the release to 100%.
145 | 10. If the violating APK is released to multiple tracks, repeat steps 5-9 in each track
146 |
147 | ## Version 0.8.6
148 | * Added `CorrelationIds` to the `Approval` object returned from the approve order call
149 |
150 | ## Version 0.8.5
151 | * Fixed a [web fallback issue](https://github.com/paypal/android-checkout-sdk/issues/153) causing an infinite spinner when coming back from web fallbacks flows.
152 | * Fixed two issues related to auth native and legacy web auth flows.
153 |
154 | ## Version 0.8.4
155 | * Added the `phone`, `birthDate` and `taxInfo` fields to the `Payer`object that comes as part of the `OrderResponse` object in `getOrderDetails()`, `capture()` and `authorize()` order actions.
156 |
157 | ## Version 0.8.3
158 | * Added `getOrderDetails()` to `OrderActions` in the onApprove callback for getting order information after an order has been approved
159 |
160 | ## Version 0.8.2
161 | * Added the ability to accept a shipping change without having to patching the order
162 | * Resolved crashes caused by Encrypted Shared Preferences
163 | * Fixed an issue caused by a duplicate package name when minifying
164 | * Accessibility fixes
165 |
166 | ## Version 0.8.1
167 | * Removing the requirement for a returnUrl(If there are issues with returning back to the original app, try adding *nativexo://paypalpay* as another returnUrl on the developer portal)
168 | * Adding the ability to add a card on US and EU
169 | * Fixing memory leak issues
170 |
171 | ## Version 0.8.0
172 | * Adding in support for native one time password
173 | * Adding in support for checking out with crypto
174 |
175 | ## Version 0.7.3
176 | * The original returnUrl was added back into this release and needs to be set in the merchant portal if it wasn't already set
177 |
178 | ## Version 0.7.2
179 | * This version introduces a breaking change around removing the use for a returnUrl
180 | * In order to get authenticatation working correctly, there needs to be a returnUrl of *nativexo://paypalpay* added to the clientId of the app
181 | * If this returnUrl is not added the app will not redirect correctly after auth
182 |
183 | ## Version 0.7.1
184 | * Adding in native support for Billing Agreements
185 | ``` fun setBillingAgreementId(billingAgreementId: String) {
186 | CoroutineScope(coroutineContext).launch {
187 | val convertedEcToken = try {
188 | baTokenToEcTokenAction.execute(billingAgreementId)
189 | } catch (exception: Exception) {
190 | internalOnOrderCreated(
191 | OrderCreateResult.Error(
192 | PYPLException("exception with setting BA id: ${exception.message}")
193 | )
194 | )
195 | null
196 | }
197 | convertedEcToken?.let {
198 | with(DebugConfigManager.getInstance()) {
199 | checkoutToken = convertedEcToken
200 | repo.isVaultFlow = false
201 | applicationContext?.let { Cache.cacheIsVaultFlow(it, repo.isVaultFlow) }
202 | internalOnOrderCreated(OrderCreateResult.Success(convertedEcToken))
203 | }
204 | }
205 | }
206 | }
207 | ```
208 | ## Version 0.7.0
209 | * Extracting 3ds and cardinal into its own optional module controlled by build flavors
210 | ```
211 | productFlavors {
212 | external {
213 | dimension "clientType"
214 | }
215 | }
216 | ```
217 | * Adding in native smart payment buttons
218 | * Adding in support for Overcapture
219 | * UI updates
220 |
221 | ## Version 0.6.3
222 | * Updated paypal authentication SDK to 1.6.0
223 | * Resolved refresh token to access token exchange for subsequent logins use cases
224 |
225 | ## Version 0.6.2
226 | * Updated Proguard rules resolving a crash related to EncryptedSharedPreferences on minified builds
227 | * OnShippingChange callback is now invoked when the checkout experience is launched
228 | * Added ability to customize the cancellation dialog shown to users
229 |
230 | ```kotlin
231 | val config = CheckoutConfig(
232 | ...
233 | uiConfig = UIConfig(
234 | showExitSurveyDialog = true
235 | )
236 | )
237 | ```
238 |
239 | ## Version 0.6.1
240 | * Added support for v2/vault web fallback
241 | * Resolves a navigation issue specific to Android 6
242 | * Animation updates
243 |
244 | ### Required Proguard Rules for 0.6.1
245 | ```
246 | -keepclassmembers class * extends com.google.crypto.tink.shaded.protobuf.GeneratedMessageLite {
247 | ;
248 | }
249 | ```
250 | There's an issue with EncryptedSharedPreferences where the library is missing a required proguard rule. The temporary fix is to include the above rule in your app module's `proguard-rules.pro` file. Future versions of the SDK will automatically include this rule.
251 |
252 | ## Version 0.6.0
253 | * SDK now targets Android 12 version (API 31) as well as Java 11.
254 | * Resolved a crash that happens when the app gets destroyed and then re-created by Android OS due to a config change.
255 | * Simplified Amount model returned in the OnApprove callback.
256 |
257 | Note: There are some functions that were deprecated and some return types that were changed. Please refer to the upgrade guide when migrating from an earlier version: https://developer.paypal.com/sdk/in-app/android/upgrade/
258 |
259 | ## Version 0.5.4
260 | * Resolved an issue with the shipping callback not getting invoked
261 | * Resolved a crash caused by internal instrumentation
262 | * Resolved a crash related gradients on Android 6
263 | * Added a web checkout fallback for when there is a 3DS challenge
264 |
265 | ## Version 0.5.2
266 | * Improvements to logging
267 | * Resolved a crash in the address selection screen
268 | * Updated to AndroidX libraries - Jetifier is no longer needed.
269 |
270 | ## Version 0.5.1
271 | * Resolved a crash in the buyer authentication process
272 |
273 | ## Version 0.5.0
274 | * Added OnShippingChanged callback
275 | * Resolved a bug where adding a new shipping address would break the checkout experience
276 | * Added additional error information in the OnError callback
277 |
278 | ## Version 0.4.5
279 | * Fix bug for disappearing payment button
280 | * Added payee info to order
281 |
282 | ## Version 0.4.4
283 | * Resolved a crash caused by registering network callbacks on Android 11
284 |
285 | ## Version 0.4.3
286 | * Resolved a crash caused by null checkout session
287 | * Resolved conflicting attribute names
288 | * Added buyer's name, phone and address to Approval
289 |
290 | ## Version 0.4.2
291 | * Resolved a crash caused by the funding eligibility call
292 |
293 | ## Version 0.4.1
294 | * Resolved a bug where rapid, multiple clicks of the payment button would stop the checkout flow
295 | * Resolved a bug where setting the config in the Application class would render payment buttons to be ineligible
296 |
297 | ## Version 0.4.0
298 | * Invoking the SDK now requires API level 23 and up (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
299 | * Added `paymentButtonIntent` to `CheckoutConfig`
300 | * `Cart` and `Buyer` are now returned in `OnApproves`'s `ApprovalData`
301 | * `ProcessingInstruction` can be set when creating an order client side.
302 | * Resolved a bug where the SDK would crash with `kotlin.UninitializedPropertyAccessException: lateinit property accessToken has not been initialized`
303 |
304 | ## Version 0.3.1
305 |
306 | * Resolved a bug where the SDK would crash when the buyer tries to authenticate with PayPal
307 |
308 | ## Version 0.3.0
309 |
310 | * Added the ability to pass in a Billing Agreement token through `CreateOrderActions`
311 | * Added the ability to patch an order through `OrderActions` in the `OnApprove` callback
312 | * Resolved a bug where the SDK would invoke the `OnError` callback on a background thread
313 |
314 | ## Version 0.2.0
315 | * Added Cardinal to support 3DS, this will require adding a private maven repository in order to import the SDK.
316 |
317 | ```groovy
318 | url "https://cardinalcommerceprod.jfrog.io/artifactory/android"
319 | credentials {
320 | // Be sure to add these non-sensitive credentials in order to retrieve dependencies related to the Cardinal SDK.
321 | username 'paypal_sgerritz'
322 | password 'AKCp8jQ8tAahqpT5JjZ4FRP2mW7GMoFZ674kGqHmupTesKeAY2G8NcmPKLuTxTGkKjDLRzDUQ'
323 | }
324 | ```
325 |
326 | * Added the ability to cancel checkout through `CreateOrderActions`, this is useful if an error occurs while generating an Order ID from a server-side integration.
327 | * Resolved a bug where the SDK would crash if a buyer clicked the "Cancel checkout and Return" text while authenticating.
328 | * Resolved a bug where the SDK would occasionally get stuck after the buyer approved an order.
329 |
330 | ## Version 0.1.0
331 | Initial release. Please see [official documentation](https://developer.paypal.com/docs/business/native-checkout/android/) for full integration steps.
332 |
333 | * Added `PayPalCheckout` as one of the main interfaces for launching the pay sheet.
334 | * Added `PaymentButton` along with `PayPalButton`, `PayLaterButton`, and `PayPalCreditButton` which can also be used to launch the pay sheet (this is the preferred option as well).
335 | * Added `CreateOrder` interface which allows for orders to be created with both client-side and server-side integrations.
336 | * Added `OnApprove` interface which notifies the client when a buyer approves an order, at this point the client application can either `Capture` or `Authorize` an order.
337 | * Added `OnError` interface which notifies the client when a terminal error occurred in the experience, in these situations the pay sheet will be dismissed.
338 | * Added `OnCancel` interface which notifies the client application that a buyer cancelled out of the experience.
339 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | https://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "{}"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright 2020 PayPal
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | https://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # PayPal Checkout Samples for Android
2 |
3 |  
4 |
5 | ### Notice:
6 | This integration path is only active for existing developers who have previously integrated the PayPal Android Checkout SDK. For any new developers seeking the Native Checkout experience, this integration path is considered inactive. Please integrate via [BrainTree Android SDK](https://github.com/braintree/braintree_android) or [PayPal Android SDK](https://github.com/paypal/Android-SDK/).
7 |
8 | ---
9 |
10 | This repository contains various sample applications for the PayPal Checkout SDK for Android. If you have questions, comments, or ideas related to the Android Checkout SDK or the sample apps please create a new [issue](https://github.com/paypal/paypalcheckout-samples-android/issues) if one related to your question does not already exist.
11 |
12 | ### Android Version Requirement
13 | The SDK will work with apps that have a minimum version of 21. However, to launch the PayPal flow,
14 | a check for Android 23 or higher must be done.
15 |
16 | ```kotlin
17 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
18 | PayPalCheckout.setConfig(...)
19 | } else {
20 | Toast.makeText(this, "Checkout SDK only available for API 23+", Toast.LENGTH_SHORT).show()
21 | }
22 |
23 | ...
24 |
25 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
26 | PayPalCheckout.startCheckout(...)
27 | } else {
28 | Toast.makeText(this, "Checkout SDK only available for API 23+", Toast.LENGTH_SHORT).show()
29 | }
30 | ```
31 |
32 | ## Sample App Preparation
33 |
34 | The sample project is intended to be as hands off as possible. With that in mind, there are only two
35 | values which are required to this sample app and they include:
36 |
37 | 1. An app client ID. This is used by the CheckoutConfig and ensures your application can authorize
38 | customers to place orders.
39 | 2. A corresponding app secret. This is required for generating payment tokens. This is not required
40 | for your own implementation of the PayPal Checkout SDK and is only used to illustrate how you could
41 | generate tokens for customer orders with a backend system.
42 | 3. Setting a return URL.
43 |
44 | Please reference our [developer documentation](https://developer.paypal.com/docs/business/native-checkout/android/)
45 | overview to learn about how to create a new PayPal application as well as how to find those details. **At this time, the SDK is in limited release so please be sure to follow all of the steps outlined.**
46 | Once you have the credentials available you will want to add them to `QuickStartConstants.kt`.
47 |
48 | ```kotlin
49 | // QuickStartConstants.kt
50 | const val PAYPAL_CLIENT_ID = "YOUR-CLIENT-ID-HERE"
51 | const val PAYPAL_SECRET = "ONLY-FOR-QUICKSTART-DO-NOT-INCLUDE-SECRET-IN-CLIENT-SIDE-APPLICATIONS"
52 | ```
53 |
54 | ### Setting a Return URL
55 |
56 | A return URL is required for redirecting users back to the sample app after authenticating. For more details on setting a return URL please see our [developer documentation](https://developer.paypal.com/docs/business/native-checkout/android/#know-before-you-code),
57 | however instead of setting the Live App Settings you want to ensure you are setting your Sandbox App Settings. The return URL you should use is `com.paypal.checkoutsamples://paypalpay`.
58 |
59 | ## Releases
60 |
61 | New versions of the Android Checkout SDK are published via MavenCentral. Please refer to the badge at the top of this repository for the latest version of the SDK. Please see our [change log](CHANGELOG.md) to understand what changed from one version to the next.
62 |
63 | ### Adding Dependency via Gradle Groovy DSL
64 | ```groovy
65 | implementation 'com.paypal.checkout:android-sdk:'
66 | ```
67 |
68 | ### Adding Dependency via Gradle Kotlin DSL
69 | ```kotlin
70 | implementation("com.paypal.checkout:android-sdk:")
71 | ```
72 |
73 | ### Snapshots
74 |
75 | Snapshot builds are available [through Sonatype](https://oss.sonatype.org/content/repositories/snapshots/) and can be used for early testing of new features or validating a reported issue has been resolved. **Snapshots should not be considered stable or production ready**. Please use the latest stable release of the Android Checkout SDK for production builds.
76 |
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 | buildscript {
3 | ext.kotlin_version = "1.6.10"
4 | repositories {
5 | google()
6 | mavenCentral()
7 | }
8 | dependencies {
9 | classpath 'com.android.tools.build:gradle:7.1.2'
10 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
11 | classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version"
12 |
13 | // NOTE: Do not place your application dependencies here; they belong
14 | // in the individual module build.gradle files
15 | }
16 | }
17 |
18 | allprojects {
19 | repositories {
20 | google()
21 | mavenCentral()
22 | maven {
23 | url 'https://oss.sonatype.org/content/repositories/snapshots/'
24 | }
25 | maven {
26 | url "https://cardinalcommerceprod.jfrog.io/artifactory/android"
27 | credentials {
28 | // Be sure to add these non-sensitive credentials in order to retrieve dependencies from
29 | // the private repository.
30 | username 'paypal_sgerritz'
31 | password 'AKCp8jQ8tAahqpT5JjZ4FRP2mW7GMoFZ674kGqHmupTesKeAY2G8NcmPKLuTxTGkKjDLRzDUQ'
32 | }
33 | }
34 | mavenLocal()
35 | }
36 |
37 | configurations.all {
38 | resolutionStrategy.cacheChangingModulesFor 0, 'seconds'
39 | }
40 | }
41 |
42 | task clean(type: Delete) {
43 | delete rootProject.buildDir
44 | }
45 |
--------------------------------------------------------------------------------
/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
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.enableJetifier=true
18 | android.useAndroidX=true
19 | # Kotlin code style for this project: "official" or "obsolete":
20 | kotlin.code.style=official
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/paypal/android-checkout-sdk/991dd4c462bfc954c29e1b8528d2e4ae043ee99f/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Tue Mar 30 09:15:39 CDT 2021
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-all.zip
7 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Attempt to set APP_HOME
10 | # Resolve links: $0 may be a link
11 | PRG="$0"
12 | # Need this for relative symlinks.
13 | while [ -h "$PRG" ] ; do
14 | ls=`ls -ld "$PRG"`
15 | link=`expr "$ls" : '.*-> \(.*\)$'`
16 | if expr "$link" : '/.*' > /dev/null; then
17 | PRG="$link"
18 | else
19 | PRG=`dirname "$PRG"`"/$link"
20 | fi
21 | done
22 | SAVED="`pwd`"
23 | cd "`dirname \"$PRG\"`/" >/dev/null
24 | APP_HOME="`pwd -P`"
25 | cd "$SAVED" >/dev/null
26 |
27 | APP_NAME="Gradle"
28 | APP_BASE_NAME=`basename "$0"`
29 |
30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
31 | DEFAULT_JVM_OPTS=""
32 |
33 | # Use the maximum available, or set MAX_FD != -1 to use that value.
34 | MAX_FD="maximum"
35 |
36 | warn () {
37 | echo "$*"
38 | }
39 |
40 | die () {
41 | echo
42 | echo "$*"
43 | echo
44 | exit 1
45 | }
46 |
47 | # OS specific support (must be 'true' or 'false').
48 | cygwin=false
49 | msys=false
50 | darwin=false
51 | nonstop=false
52 | case "`uname`" in
53 | CYGWIN* )
54 | cygwin=true
55 | ;;
56 | Darwin* )
57 | darwin=true
58 | ;;
59 | MINGW* )
60 | msys=true
61 | ;;
62 | NONSTOP* )
63 | nonstop=true
64 | ;;
65 | esac
66 |
67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
68 |
69 | # Determine the Java command to use to start the JVM.
70 | if [ -n "$JAVA_HOME" ] ; then
71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
72 | # IBM's JDK on AIX uses strange locations for the executables
73 | JAVACMD="$JAVA_HOME/jre/sh/java"
74 | else
75 | JAVACMD="$JAVA_HOME/bin/java"
76 | fi
77 | if [ ! -x "$JAVACMD" ] ; then
78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
79 |
80 | Please set the JAVA_HOME variable in your environment to match the
81 | location of your Java installation."
82 | fi
83 | else
84 | JAVACMD="java"
85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
86 |
87 | Please set the JAVA_HOME variable in your environment to match the
88 | location of your Java installation."
89 | fi
90 |
91 | # Increase the maximum file descriptors if we can.
92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
93 | MAX_FD_LIMIT=`ulimit -H -n`
94 | if [ $? -eq 0 ] ; then
95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
96 | MAX_FD="$MAX_FD_LIMIT"
97 | fi
98 | ulimit -n $MAX_FD
99 | if [ $? -ne 0 ] ; then
100 | warn "Could not set maximum file descriptor limit: $MAX_FD"
101 | fi
102 | else
103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
104 | fi
105 | fi
106 |
107 | # For Darwin, add options to specify how the application appears in the dock
108 | if $darwin; then
109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
110 | fi
111 |
112 | # For Cygwin, switch paths to Windows format before running java
113 | if $cygwin ; then
114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
116 | JAVACMD=`cygpath --unix "$JAVACMD"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Escape application args
158 | save () {
159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
160 | echo " "
161 | }
162 | APP_ARGS=$(save "$@")
163 |
164 | # Collect all arguments for the java command, following the shell quoting and substitution rules
165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
166 |
167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
169 | cd "$(dirname "$0")"
170 | fi
171 |
172 | exec "$JAVACMD" "$@"
173 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | set DIRNAME=%~dp0
12 | if "%DIRNAME%" == "" set DIRNAME=.
13 | set APP_BASE_NAME=%~n0
14 | set APP_HOME=%DIRNAME%
15 |
16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
17 | set DEFAULT_JVM_OPTS=
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windows variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 |
53 | :win9xME_args
54 | @rem Slurp the command line arguments.
55 | set CMD_LINE_ARGS=
56 | set _SKIP=2
57 |
58 | :win9xME_args_slurp
59 | if "x%~1" == "x" goto execute
60 |
61 | set CMD_LINE_ARGS=%*
62 |
63 | :execute
64 | @rem Setup the command line
65 |
66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
67 |
68 | @rem Execute Gradle
69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
70 |
71 | :end
72 | @rem End local scope for the variables with windows NT shell
73 | if "%ERRORLEVEL%"=="0" goto mainEnd
74 |
75 | :fail
76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
77 | rem the _cmd.exe /c_ return code!
78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
79 | exit /b 1
80 |
81 | :mainEnd
82 | if "%OS%"=="Windows_NT" endlocal
83 |
84 | :omega
85 |
--------------------------------------------------------------------------------
/quickstart-kotlin/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/quickstart-kotlin/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 | apply plugin: 'kotlin-android'
3 | apply plugin: 'kotlin-android-extensions'
4 | apply plugin: 'kotlinx-serialization'
5 |
6 | android {
7 | compileSdkVersion 31
8 |
9 | defaultConfig {
10 | applicationId "com.paypal.checkoutsamples"
11 | minSdkVersion 21
12 | targetSdkVersion 31
13 | versionCode 1
14 | versionName "1.0"
15 |
16 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
17 | }
18 |
19 | buildTypes {
20 | release {
21 | minifyEnabled false
22 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
23 | }
24 | }
25 |
26 | // Required For Checkouts SDK To Work Properly (OkHttp Requirement) - https://stackoverflow.com/a/59448917
27 | compileOptions {
28 | sourceCompatibility JavaVersion.VERSION_1_8
29 | targetCompatibility JavaVersion.VERSION_1_8
30 |
31 | // Used for the sample app (Kotlin Serialization Library) not required for Checkouts SDK.
32 | kotlinOptions {
33 | freeCompilerArgs += "-Xopt-in=org.mylibrary.OptInAnnotation"
34 | }
35 | }
36 |
37 | kotlinOptions {
38 | jvmTarget = "1.8"
39 | }
40 | }
41 |
42 | dependencies {
43 | // PayPal Checkout SDK Libraries
44 | implementation 'com.paypal.checkout:android-sdk:1.3.2'
45 |
46 | // Other Dependencies Related To Sample App
47 | implementation fileTree(dir: "libs", include: ["*.jar"])
48 | implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
49 | implementation 'androidx.core:core-ktx:1.7.0'
50 | implementation 'androidx.appcompat:appcompat:1.4.1'
51 | implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
52 | implementation 'com.squareup.retrofit2:retrofit:2.9.0'
53 | implementation 'com.jakewharton.retrofit:retrofit2-kotlinx-serialization-converter:0.8.0'
54 | implementation 'org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.0'
55 | implementation 'com.google.android.material:material:1.5.0'
56 | implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.0'
57 | }
58 |
--------------------------------------------------------------------------------
/quickstart-kotlin/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
--------------------------------------------------------------------------------
/quickstart-kotlin/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
15 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
27 |
30 |
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/quickstart-kotlin/src/main/java/com/paypal/checkoutsamples/KotlinQuickStartActivity.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.checkoutsamples
2 |
3 | import android.os.Bundle
4 | import androidx.appcompat.app.AppCompatActivity
5 | import com.google.android.material.snackbar.Snackbar
6 | import com.paypal.checkoutsamples.order.OrdersQuickStartActivity
7 | import com.paypal.checkoutsamples.paymentbutton.PaymentButtonQuickStartActivity
8 | import com.paypal.checkoutsamples.token.TokenQuickStartActivity
9 | import kotlinx.android.synthetic.main.activity_kotlin_quick_start.*
10 |
11 | class KotlinQuickStartActivity : AppCompatActivity() {
12 |
13 | private val clientIdWasUpdated by lazy {
14 | PAYPAL_CLIENT_ID != "YOUR-CLIENT-ID-HERE"
15 | }
16 |
17 | private val secretWasUpdated by lazy {
18 | PAYPAL_SECRET != "ONLY-FOR-QUICKSTART-DO-NOT-INCLUDE-SECRET-IN-CLIENT-SIDE-APPLICATIONS"
19 | }
20 |
21 | override fun onCreate(savedInstanceState: Bundle?) {
22 | super.onCreate(savedInstanceState)
23 | setContentView(R.layout.activity_kotlin_quick_start)
24 |
25 | buyWithOrder.setOnClickListener {
26 | if (clientIdWasUpdated) {
27 | startActivity(OrdersQuickStartActivity.startIntent(this))
28 | } else {
29 | displayErrorSnackbar("Please Update PAYPAL_CLIENT_ID In QuickStartConstants.")
30 | }
31 | }
32 |
33 | buyWithOrderToken.setOnClickListener {
34 | if (clientIdWasUpdated && secretWasUpdated) {
35 | startActivity(TokenQuickStartActivity.startIntent(this))
36 | } else {
37 | displayErrorSnackbar("Please Update PAYPAL_CLIENT_ID and PAYPAL_SECRET In QuickStartConstants.")
38 | }
39 | }
40 |
41 | buyWithPaymentButton.setOnClickListener {
42 | if (clientIdWasUpdated) {
43 | startActivity(PaymentButtonQuickStartActivity.startIntent(this))
44 | } else {
45 | displayErrorSnackbar("Please Update PAYPAL_CLIENT_ID In QuickStartConstants.")
46 | }
47 | }
48 | }
49 |
50 | private fun displayErrorSnackbar(errorMessage: String) {
51 | Snackbar.make(rootQuickStart, errorMessage, Snackbar.LENGTH_INDEFINITE)
52 | .apply { setAction("Got It 👍") { dismiss() } }
53 | .show()
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/quickstart-kotlin/src/main/java/com/paypal/checkoutsamples/QuickStartApp.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.checkoutsamples
2 |
3 | import android.app.Application
4 | import com.paypal.checkout.PayPalCheckout
5 | import com.paypal.checkout.config.CheckoutConfig
6 | import com.paypal.checkout.config.Environment
7 | import com.paypal.checkout.config.SettingsConfig
8 | import com.paypal.checkout.createorder.CurrencyCode
9 | import com.paypal.checkout.createorder.UserAction
10 |
11 | class QuickStartApp : Application() {
12 | override fun onCreate() {
13 | super.onCreate()
14 | PayPalCheckout.setConfig(
15 | checkoutConfig = CheckoutConfig(
16 | application = this,
17 | clientId = PAYPAL_CLIENT_ID,
18 | environment = Environment.SANDBOX,
19 | currencyCode = CurrencyCode.USD,
20 | userAction = UserAction.PAY_NOW,
21 | settingsConfig = SettingsConfig(
22 | loggingEnabled = true,
23 | showWebCheckout = false
24 | ),
25 | returnUrl = "${BuildConfig.APPLICATION_ID}://paypalpay"
26 | )
27 | )
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/quickstart-kotlin/src/main/java/com/paypal/checkoutsamples/QuickStartConstants.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.checkoutsamples
2 |
3 | /**
4 | * You can get a Client ID by signing into your PayPal developer account or by signing up for one if
5 | * you haven't already.
6 | *
7 | * @see [Developer Portal](https://developer.paypal.com/developer/accounts/)
8 | * @see [Managing Sandbox Apps](https://developer.paypal.com/docs/api-basics/manage-apps/#create-or-edit-sandbox-and-live-apps)
9 | */
10 | const val PAYPAL_CLIENT_ID = "YOUR-CLIENT-ID-HERE"
11 |
12 | /**
13 | * You can get a Secret by signing into your PayPal developer account or by signing up for one if
14 | * you haven't already.
15 | *
16 | * NOTE: This is only required for the samples which are generating tokens and you should not include
17 | * your secret in development or production Android apps.
18 | *
19 | * @see [Developer Portal](https://developer.paypal.com/developer/accounts/)
20 | * @see [Managing Sandbox Apps](https://developer.paypal.com/docs/api-basics/manage-apps/#create-or-edit-sandbox-and-live-apps)
21 | */
22 | const val PAYPAL_SECRET = "ONLY-FOR-QUICKSTART-DO-NOT-INCLUDE-SECRET-IN-CLIENT-SIDE-APPLICATIONS"
23 |
--------------------------------------------------------------------------------
/quickstart-kotlin/src/main/java/com/paypal/checkoutsamples/order/CreateItemDialog.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.checkoutsamples.order
2 |
3 | import android.annotation.SuppressLint
4 | import android.app.AlertDialog
5 | import android.app.Dialog
6 | import android.os.Bundle
7 | import android.view.View
8 | import androidx.fragment.app.DialogFragment
9 | import com.google.android.material.textfield.TextInputLayout
10 | import com.paypal.checkout.createorder.ItemCategory
11 | import com.paypal.checkoutsamples.R
12 | import kotlinx.android.synthetic.main.dialog_create_item.view.*
13 |
14 | /**
15 | * CreateItemDialog provides an entry point for the user to create a new item and for the host view
16 | * to be notified when the item is created along with it's contents.
17 | *
18 | * @see [CreatedItem]
19 | */
20 | class CreateItemDialog : DialogFragment() {
21 |
22 | var onItemCreated: ((createdItem: CreatedItem) -> Unit)? = null
23 |
24 | @SuppressLint("InflateParams")
25 | override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
26 | return activity?.let { nonNullActivity ->
27 | val dialogBuilder = AlertDialog.Builder(nonNullActivity)
28 | val layoutInflater = nonNullActivity.layoutInflater
29 | val dialogView = layoutInflater.inflate(R.layout.dialog_create_item, null)
30 |
31 | with(dialogView) {
32 | selectItemCategory.setOnCheckedChangeListener { _, _ -> errorTextView.text = "" }
33 |
34 | createItemButton.setOnClickListener {
35 | if (!canSaveItem(dialogView)) return@setOnClickListener
36 |
37 | onItemCreated?.invoke(
38 | CreatedItem(
39 | name = itemNameInput.text,
40 | quantity = itemQuantityInput.text,
41 | amount = itemAmountInput.text,
42 | taxAmount = itemTaxInput.text,
43 | itemCategory = selectedItemCategory(selectItemCategory.checkedRadioButtonId)
44 | )
45 | )
46 |
47 | dismiss()
48 | }
49 | }
50 |
51 | dialogBuilder.setView(dialogView).create()
52 | } ?: throw IllegalStateException("Activity cannot be null.")
53 | }
54 |
55 | private fun canSaveItem(view: View): Boolean = with(view) {
56 | itemNameInput.validateField()
57 | itemQuantityInput.validateField()
58 | itemAmountInput.validateField()
59 | itemTaxInput.validateField()
60 |
61 | if (!itemCategoryPhysicalGoods.isChecked && !itemCategoryDigitalGoods.isChecked) {
62 | errorTextView.text = getString(R.string.dialog_create_error_item_category)
63 | }
64 |
65 | return itemNameInput.text.isNotEmpty() && itemQuantityInput.text.isNotEmpty()
66 | && itemAmountInput.text.isNotEmpty() && itemTaxInput.text.isNotEmpty()
67 | && (itemCategoryPhysicalGoods.isChecked || itemCategoryDigitalGoods.isChecked)
68 | }
69 |
70 | private fun TextInputLayout.validateField() {
71 | if (text.isEmpty()) error = getString(R.string.dialog_create_error_required)
72 | }
73 |
74 | private fun selectedItemCategory(selectedId: Int): ItemCategory {
75 | return when (selectedId) {
76 | R.id.itemCategoryPhysicalGoods -> ItemCategory.PHYSICAL_GOODS
77 | R.id.itemCategoryDigitalGoods -> ItemCategory.DIGITAL_GOODS
78 | else -> {
79 | throw IllegalArgumentException(
80 | "Expected one of the following ids: ${R.id.itemCategoryPhysicalGoods}, or " +
81 | "${R.id.itemCategoryDigitalGoods} but was $selectedId"
82 | )
83 | }
84 | }
85 | }
86 |
87 | private val TextInputLayout.text: String
88 | get() {
89 | return editText?.run { text.toString() } ?: ""
90 | }
91 |
92 | }
93 |
94 | /**
95 | * CreatedItem is a simple data class which is used for sending [CreatedItem] details from one input
96 | * screen to another.
97 | */
98 | data class CreatedItem(
99 | val name: String,
100 | val quantity: String,
101 | val amount: String,
102 | val taxAmount: String,
103 | val itemCategory: ItemCategory
104 | )
105 |
--------------------------------------------------------------------------------
/quickstart-kotlin/src/main/java/com/paypal/checkoutsamples/order/OrdersQuickStartActivity.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.checkoutsamples.order
2 |
3 | import android.annotation.SuppressLint
4 | import android.content.Context
5 | import android.content.Intent
6 | import android.os.Bundle
7 | import android.util.Log
8 | import android.view.View
9 | import androidx.appcompat.app.AppCompatActivity
10 | import com.google.android.material.snackbar.Snackbar
11 | import com.paypal.checkout.PayPalCheckout
12 | import com.paypal.checkout.approve.OnApprove
13 | import com.paypal.checkout.cancel.OnCancel
14 | import com.paypal.checkout.createorder.CreateOrder
15 | import com.paypal.checkout.createorder.CurrencyCode
16 | import com.paypal.checkout.createorder.OrderIntent
17 | import com.paypal.checkout.createorder.ShippingPreference
18 | import com.paypal.checkout.createorder.UserAction
19 | import com.paypal.checkout.error.OnError
20 | import com.paypal.checkout.order.AuthorizeOrderResult
21 | import com.paypal.checkout.order.CaptureOrderResult
22 | import com.paypal.checkoutsamples.R
23 | import com.paypal.checkoutsamples.order.usecase.CreateOrderRequest
24 | import com.paypal.checkoutsamples.order.usecase.CreateOrderUseCase
25 | import kotlinx.android.synthetic.main.activity_orders_quick_start.*
26 | import kotlinx.android.synthetic.main.item_preview_item.view.*
27 |
28 | class OrdersQuickStartActivity : AppCompatActivity() {
29 |
30 | private val tag = javaClass.simpleName
31 |
32 | private val checkoutSdk: PayPalCheckout
33 | get() = PayPalCheckout
34 |
35 | private val selectedUserAction: UserAction
36 | get() {
37 | return when (val selectedId = selectUserAction.checkedRadioButtonId) {
38 | R.id.userActionOptionContinue -> UserAction.CONTINUE
39 | R.id.userActionOptionPayNow -> UserAction.PAY_NOW
40 | else -> {
41 | throw IllegalArgumentException(
42 | "Expected one of the following ids: ${R.id.userActionOptionContinue}, or " +
43 | "${R.id.userActionOptionPayNow} but was $selectedId"
44 | )
45 | }
46 | }
47 | }
48 |
49 | private val selectedOrderIntent: OrderIntent
50 | get() {
51 | return when (val selectedId = selectOrderIntent.checkedRadioButtonId) {
52 | R.id.orderIntentOptionAuthorize -> OrderIntent.AUTHORIZE
53 | R.id.orderIntentOptionCapture -> OrderIntent.CAPTURE
54 | else -> {
55 | throw IllegalArgumentException(
56 | "Expected one of the following ids: ${R.id.orderIntentOptionAuthorize}, or " +
57 | "${R.id.orderIntentOptionCapture} but was $selectedId"
58 | )
59 | }
60 | }
61 | }
62 |
63 | private val selectedShippingPreference: ShippingPreference
64 | get() {
65 | return when (val selectedId = selectShippingPreference.checkedRadioButtonId) {
66 | R.id.shippingPreferenceOptionGetFromFile -> ShippingPreference.GET_FROM_FILE
67 | R.id.shippingPreferenceOptionNoShipping -> ShippingPreference.NO_SHIPPING
68 | R.id.shippingPreferenceOptionSetProvidedAddress -> ShippingPreference.SET_PROVIDED_ADDRESS
69 | else -> {
70 | throw IllegalArgumentException(
71 | "Expected one of the following ids: ${R.id.shippingPreferenceOptionGetFromFile}, " +
72 | "${R.id.shippingPreferenceOptionNoShipping}, or " +
73 | "${R.id.shippingPreferenceOptionSetProvidedAddress} but was $selectedId"
74 | )
75 | }
76 | }
77 | }
78 |
79 | private val selectedCurrencyCode: CurrencyCode
80 | get() {
81 | return when (val selectedId = selectCurrencyCode.checkedRadioButtonId) {
82 | R.id.currencyCodeUsd -> CurrencyCode.USD
83 | R.id.currencyCodeEur -> CurrencyCode.EUR
84 | R.id.currencyCodeGbp -> CurrencyCode.GBP
85 | else -> {
86 | throw IllegalArgumentException(
87 | "Expected one of the following ids: ${R.id.currencyCodeUsd}, " +
88 | "${R.id.currencyCodeEur}, or ${R.id.currencyCodeGbp} but was $selectedId"
89 | )
90 | }
91 | }
92 | }
93 |
94 | private val createItemDialog: CreateItemDialog by lazy {
95 | CreateItemDialog().apply { onItemCreated = ::onItemCreated }
96 | }
97 |
98 | private val createdItems = mutableListOf()
99 |
100 | private val createOrderUseCase by lazy { CreateOrderUseCase() }
101 |
102 | override fun onCreate(savedInstanceState: Bundle?) = with(applicationContext) {
103 | super.onCreate(savedInstanceState)
104 | setContentView(R.layout.activity_orders_quick_start)
105 |
106 | checkoutSdk.registerCallbacks(
107 | onApprove = OnApprove { approval ->
108 | Log.i(tag, "OnApprove: $approval")
109 | when (selectedOrderIntent) {
110 | OrderIntent.AUTHORIZE -> approval.orderActions.authorize { result ->
111 | val message = when (result) {
112 | is AuthorizeOrderResult.Success -> {
113 | Log.i(tag, "Success: $result")
114 | "💰 Order Authorization Succeeded 💰"
115 | }
116 | is AuthorizeOrderResult.Error -> {
117 | Log.i(tag, "Error: $result")
118 | "🔥 Order Authorization Failed 🔥"
119 | }
120 | }
121 | showSnackbar(message)
122 | }
123 | OrderIntent.CAPTURE -> approval.orderActions.capture { result ->
124 | val message = when (result) {
125 | is CaptureOrderResult.Success -> {
126 | Log.i(tag, "Success: $result")
127 | "💰 Order Capture Succeeded 💰"
128 | }
129 | is CaptureOrderResult.Error -> {
130 | Log.i(tag, "Error: $result")
131 | "🔥 Order Capture Failed 🔥"
132 | }
133 | }
134 | showSnackbar(message)
135 | }
136 | }
137 | },
138 | onCancel = OnCancel {
139 | Log.d(tag, "OnCancel")
140 | showSnackbar("😭 Buyer Cancelled Checkout 😭")
141 | },
142 | onError = OnError { errorInfo ->
143 | Log.d(tag, "ErrorInfo: $errorInfo")
144 | showSnackbar("🚨 An Error Occurred 🚨")
145 | }
146 | )
147 |
148 | addItemButton.setOnClickListener {
149 | createItemDialog.show(supportFragmentManager, "CreateItemDialog")
150 | }
151 | submitOrderButton.setOnClickListener {
152 | if (createdItems.isEmpty()) {
153 | itemErrorTextView.visibility = View.VISIBLE
154 | } else {
155 | startCheckoutWithSampleOrders(createdItems, selectedCurrencyCode)
156 | }
157 | }
158 | }
159 |
160 | @SuppressLint("InflateParams")
161 | private fun onItemCreated(createdItem: CreatedItem) {
162 | val itemView = layoutInflater.inflate(R.layout.item_preview_item, itemsContainer, false)
163 | .apply {
164 | itemNameText.text = createdItem.name
165 | itemAmountText.text = createdItem.amount
166 | itemTaxText.text = createdItem.taxAmount
167 | itemQuantityText.text = getString(
168 | R.string.orders_quick_start_activity_created_item_quantity,
169 | createdItem.quantity
170 | )
171 | }
172 | itemsContainer.addView(itemView)
173 |
174 | createdItems.add(createdItem)
175 | itemErrorTextView.visibility = View.GONE
176 | }
177 |
178 | private fun startCheckoutWithSampleOrders(
179 | createdItems: List,
180 | currencyCode: CurrencyCode
181 | ) {
182 | val createOrderRequest =
183 | CreateOrderRequest(
184 | orderIntent = selectedOrderIntent,
185 | userAction = selectedUserAction,
186 | shippingPreference = selectedShippingPreference,
187 | currencyCode = currencyCode,
188 | createdItems = createdItems
189 | )
190 | val order = createOrderUseCase.execute(createOrderRequest)
191 |
192 | checkoutSdk.startCheckout(
193 | createOrder = CreateOrder { actions ->
194 | actions.create(order) { id ->
195 | Log.d(tag, "Order ID: $id")
196 | }
197 | },
198 | // This sample app has not obtained consent from the buyer to collect location data.
199 | // This flag enables PayPal to collect necessary information required for Fraud Detection and Risk Management
200 | hasUserLocationConsent = false
201 | )
202 | }
203 |
204 | private fun showSnackbar(text: String) {
205 | Snackbar.make(rootOrdersQuickStart, text, Snackbar.LENGTH_LONG).show()
206 | }
207 |
208 | companion object {
209 | fun startIntent(context: Context): Intent {
210 | return Intent(context, OrdersQuickStartActivity::class.java)
211 | }
212 | }
213 | }
214 |
--------------------------------------------------------------------------------
/quickstart-kotlin/src/main/java/com/paypal/checkoutsamples/order/usecase/CreateAmountUseCase.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.checkoutsamples.order.usecase
2 |
3 | import com.paypal.checkout.createorder.CurrencyCode
4 | import com.paypal.checkout.order.Amount
5 | import com.paypal.checkout.order.BreakDown
6 | import com.paypal.checkout.order.UnitAmount
7 | import com.paypal.checkoutsamples.order.CreatedItem
8 | import java.math.BigDecimal
9 | import java.math.RoundingMode
10 | import java.text.DecimalFormat
11 |
12 | /**
13 | * CreateAmountRequest contains all of the necessary properties to successfully create an Amount with
14 | * the PayPal Checkout SDK.
15 | */
16 | data class CreateAmountRequest(
17 | val createdItems: List,
18 | val currencyCode: CurrencyCode
19 | )
20 |
21 | /**
22 | * CreateOrderUseCase provides a way to construct an [Amount] given a [CreateAmountRequest]. In
23 | * order to successfully create an [Amount] for a PurchaseUnit the value (what the customer is
24 | * charged). The following calculation is used when determining the total value:
25 | * itemTotal + taxTotal + shipping + handling + insurance - shippingDiscount - discount
26 | */
27 | class CreateAmountUseCase {
28 |
29 | fun execute(request: CreateAmountRequest): Amount = with(request) {
30 | val itemTotal = createdItems.map { it.amount.toDouble() * it.quantity.toInt() }
31 | .sum().toBigDecimal().scaledForMoney
32 | val taxTotal = createdItems.map { it.taxAmount.toDouble() * it.quantity.toInt() }
33 | .sum().toBigDecimal().scaledForMoney
34 | val shippingTotal = BigDecimal(0.00).scaledForMoney
35 | val handlingTotal = BigDecimal(0.00).scaledForMoney
36 | val shippingDiscountTotal = BigDecimal(0.00).scaledForMoney
37 | val itemDiscountTotal = BigDecimal(0.00).scaledForMoney
38 | val totalValue = itemTotal
39 | .add(taxTotal)
40 | .add(shippingTotal)
41 | .add(handlingTotal)
42 | .subtract(shippingDiscountTotal)
43 | .subtract(itemDiscountTotal)
44 |
45 | return Amount.Builder()
46 | .currencyCode(currencyCode)
47 | .value(totalValue.asMoneyString)
48 | .breakdown(
49 | BreakDown.Builder()
50 | .itemTotal(itemTotal.unitAmountFor(currencyCode))
51 | .shipping(shippingTotal.unitAmountFor(currencyCode))
52 | .handling(handlingTotal.unitAmountFor(currencyCode))
53 | .taxTotal(taxTotal.unitAmountFor(currencyCode))
54 | .shippingDiscount(shippingDiscountTotal.unitAmountFor(currencyCode))
55 | .discount(itemDiscountTotal.unitAmountFor(currencyCode))
56 | .build()
57 | )
58 | .build()
59 | }
60 |
61 | private fun BigDecimal.unitAmountFor(currencyCode: CurrencyCode): UnitAmount {
62 | return UnitAmount.Builder()
63 | .value(asMoneyString)
64 | .currencyCode(currencyCode)
65 | .build()
66 | }
67 |
68 | private val BigDecimal.asMoneyString: String
69 | get() = DecimalFormat("#0.00").format(this)
70 |
71 | private val BigDecimal.scaledForMoney: BigDecimal
72 | get() = setScale(2, RoundingMode.HALF_UP)
73 | }
74 |
--------------------------------------------------------------------------------
/quickstart-kotlin/src/main/java/com/paypal/checkoutsamples/order/usecase/CreateItemsUseCase.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.checkoutsamples.order.usecase
2 |
3 | import com.paypal.checkout.createorder.CurrencyCode
4 | import com.paypal.checkout.order.Items
5 | import com.paypal.checkout.order.UnitAmount
6 | import com.paypal.checkoutsamples.order.CreatedItem
7 |
8 | /**
9 | * CreateItemRequest contains all of the necessary properties to successfully create a list of Items
10 | * with the PayPal Checkout SDK.
11 | */
12 | data class CreateItemsRequest(
13 | val createdItems: List,
14 | val currencyCode: CurrencyCode
15 | )
16 |
17 | /**
18 | * CreateItemsUseCase provides a way to construct a [List] of [Items] given a [CreateItemsRequest].
19 | */
20 | class CreateItemsUseCase {
21 | fun execute(request: CreateItemsRequest): List = with(request) {
22 | return createdItems.map { createdItem ->
23 | Items.Builder()
24 | .name(createdItem.name)
25 | .quantity(createdItem.quantity)
26 | .category(createdItem.itemCategory)
27 | .unitAmount(
28 | UnitAmount.Builder()
29 | .value(createdItem.amount)
30 | .currencyCode(currencyCode)
31 | .build()
32 | )
33 | .tax(
34 | UnitAmount.Builder()
35 | .value(createdItem.taxAmount)
36 | .currencyCode(currencyCode)
37 | .build()
38 | )
39 | .build()
40 | }
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/quickstart-kotlin/src/main/java/com/paypal/checkoutsamples/order/usecase/CreateOrderUseCase.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.checkoutsamples.order.usecase
2 |
3 | import com.paypal.checkout.createorder.CurrencyCode
4 | import com.paypal.checkout.createorder.OrderIntent
5 | import com.paypal.checkout.createorder.ShippingPreference
6 | import com.paypal.checkout.createorder.UserAction
7 | import com.paypal.checkout.order.AppContext
8 | import com.paypal.checkout.order.OrderRequest
9 | import com.paypal.checkoutsamples.order.CreatedItem
10 |
11 | /**
12 | * CreateOrderRequest contains all of the necessary properties to successfully create an [Order] with
13 | * the PayPal Checkout SDK.
14 | */
15 | data class CreateOrderRequest(
16 | val orderIntent: OrderIntent,
17 | val userAction: UserAction,
18 | val shippingPreference: ShippingPreference,
19 | val currencyCode: CurrencyCode,
20 | val createdItems: List
21 | )
22 |
23 | /**
24 | * CreateOrderUseCase provides a way to construct an [Order] given a [CreateOrderRequest].
25 | */
26 | class CreateOrderUseCase(
27 | private val createPurchaseUnitUseCase: CreatePurchaseUnitUseCase = CreatePurchaseUnitUseCase()
28 | ) {
29 |
30 | fun execute(request: CreateOrderRequest): OrderRequest = with(request) {
31 | val createPurchaseUnitRequest = CreatePurchaseUnitRequest(
32 | createdItems = createdItems,
33 | shippingPreference = shippingPreference,
34 | currencyCode = currencyCode
35 | )
36 | val purchaseUnit = createPurchaseUnitUseCase.execute(createPurchaseUnitRequest)
37 |
38 | return OrderRequest.Builder()
39 | .intent(orderIntent)
40 | .purchaseUnitList(listOf(purchaseUnit))
41 | .appContext(
42 | AppContext.Builder()
43 | .brandName("Acme Inc")
44 | .userAction(userAction)
45 | .shippingPreference(shippingPreference)
46 | .build()
47 | )
48 | .build()
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/quickstart-kotlin/src/main/java/com/paypal/checkoutsamples/order/usecase/CreatePurchaseUnitUseCase.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.checkoutsamples.order.usecase
2 |
3 | import com.paypal.checkout.createorder.CurrencyCode
4 | import com.paypal.checkout.createorder.ShippingPreference
5 | import com.paypal.checkout.order.PurchaseUnit
6 | import com.paypal.checkoutsamples.order.CreatedItem
7 | import java.util.UUID
8 |
9 | /**
10 | * CreatePurchaseUnitRequest contains all of the necessary properties to successfully create a
11 | * [PurchaseUnit] with the PayPal Checkout SDK.
12 | */
13 | data class CreatePurchaseUnitRequest(
14 | val createdItems: List,
15 | val shippingPreference: ShippingPreference,
16 | val currencyCode: CurrencyCode
17 | )
18 |
19 | /**
20 | * CreatePurchaseUnitUseCase provides a way to construct a [PurchaseUnit] given a
21 | * [CreatePurchaseUnitRequest]. It's worth noting that a [PurchaseUnit] contains the bulk of an
22 | * Order's information as it contains items, shipping information, along with the total amount and
23 | * a breakdown of those totals.
24 | */
25 | class CreatePurchaseUnitUseCase(
26 | private val createItemsUseCase: CreateItemsUseCase = CreateItemsUseCase(),
27 | private val createShippingUseCase: CreateShippingUseCase = CreateShippingUseCase(),
28 | private val createAmountUseCase: CreateAmountUseCase = CreateAmountUseCase()
29 | ) {
30 |
31 | fun execute(request: CreatePurchaseUnitRequest): PurchaseUnit = with(request) {
32 | val createItemsRequest = CreateItemsRequest(createdItems, currencyCode)
33 | val items = createItemsUseCase.execute(createItemsRequest)
34 |
35 | val createShippingRequest = CreateShippingRequest(shippingPreference, currencyCode)
36 | val shipping = createShippingUseCase.execute(createShippingRequest)
37 |
38 | val amountRequest = CreateAmountRequest(createdItems, currencyCode)
39 | val amount = createAmountUseCase.execute(amountRequest)
40 |
41 | return PurchaseUnit.Builder()
42 | .referenceId(UUID.randomUUID().toString())
43 | .amount(amount)
44 | .items(items)
45 | /*
46 | * Omitting shipping will default to the customer's default shipping address.
47 | */
48 | .shipping(shipping)
49 | /*
50 | * The API caller-provided external ID. Used to reconcile API caller-initiated transactions
51 | * with PayPal transactions. Appears in transaction and settlement reports.
52 | */
53 | .customId("CUSTOM-123")
54 | /*
55 | * The purchase description.
56 | */
57 | .description("Purchase from Orders Quick Start")
58 | /*
59 | * The soft descriptor is the dynamic text used to construct the statement descriptor
60 | * that appears on a payer's card statement.
61 | *
62 | * Maximum Length: 22 characters
63 | */
64 | .softDescriptor("800-123-1234")
65 | .build()
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/quickstart-kotlin/src/main/java/com/paypal/checkoutsamples/order/usecase/CreateShippingUseCase.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.checkoutsamples.order.usecase
2 |
3 | import com.paypal.checkout.createorder.CurrencyCode
4 | import com.paypal.checkout.createorder.ShippingPreference
5 | import com.paypal.checkout.order.Address
6 | import com.paypal.checkout.order.Shipping
7 |
8 | /**
9 | * CreateOrderRequest contains all of the necessary properties to successfully create a [Shipping]
10 | * instance with the PayPal Checkout SDK.
11 | */
12 | data class CreateShippingRequest(
13 | val shippingPreference: ShippingPreference,
14 | val currencyCode: CurrencyCode
15 | )
16 |
17 | /**
18 | * CreateShippingUseCase provides a way to construct a [Shipping] instance given a
19 | * [CreateShippingRequest].
20 | */
21 | class CreateShippingUseCase {
22 |
23 | fun execute(request: CreateShippingRequest): Shipping {
24 | /*
25 | * Options for Shipping such as Standard, Express, Next Day, etc.
26 | *
27 | * Only supported for [ShippingPreference.GET_FROM_FILE], and displaying the shipping
28 | * options are currently disabled in 0.0.4, for now this can safely default to null for all
29 | * cases.
30 | */
31 | val shippingOptions = null
32 |
33 | return Shipping.Builder()
34 | .address(
35 | Address.Builder()
36 | /*
37 | * The first line of the address. For example, number or street. For example,
38 | * 173 Drury Lane. Required for data entry and compliance and risk checks.
39 | * Must contain the full address.
40 | * Maximum length: 300.
41 | */
42 | .addressLine1("123 Townsend St")
43 | /*
44 | * The second line of the address. For example, suite or apartment number.
45 | * Maximum length: 300.
46 | */
47 | .addressLine2("Floor 6")
48 | /*
49 | * A city, town, or village. Smaller than adminArea1
50 | */
51 | .adminArea2("San Francisco")
52 | /*
53 | * The highest level sub-division in a country, which is usually a province,
54 | * state, or ISO-3166-2 subdivision. Format for postal delivery.
55 | * For example, CA and not California. Value, by country, is:
56 | * UK. A county.
57 | * US. A state.
58 | * Canada. A province.
59 | * Japan. A prefecture.
60 | * Switzerland. A kanton.
61 | * Maximum length: 300.
62 | */
63 | .adminArea1("CA")
64 | /*
65 | * The postal code, which is the zip code or equivalent. Typically required
66 | * for countries with a postal code or an equivalent.
67 | * @see [Postal Code](https://en.wikipedia.org/wiki/Postal_code)
68 | * Maximum length: 60.
69 | */
70 | .postalCode("94107")
71 | /*
72 | * The two-character ISO 3166-1 code that identifies the country or region.
73 | *
74 | * @see [Country Codes](https://developer.paypal.com/docs/integration/direct/rest/country-codes/)
75 | */
76 | .countryCode("US")
77 | .build()
78 | )
79 | .options(shippingOptions)
80 | .build()
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/quickstart-kotlin/src/main/java/com/paypal/checkoutsamples/paymentbutton/PaymentButtonQuickStartActivity.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.checkoutsamples.paymentbutton
2 |
3 | import android.content.Context
4 | import android.content.Intent
5 | import android.os.Bundle
6 | import android.util.Log
7 | import androidx.appcompat.app.AppCompatActivity
8 | import com.paypal.checkout.approve.OnApprove
9 | import com.paypal.checkout.cancel.OnCancel
10 | import com.paypal.checkout.createorder.CreateOrder
11 | import com.paypal.checkout.createorder.CurrencyCode
12 | import com.paypal.checkout.createorder.OrderIntent
13 | import com.paypal.checkout.createorder.UserAction
14 | import com.paypal.checkout.error.OnError
15 | import com.paypal.checkout.order.Amount
16 | import com.paypal.checkout.order.AppContext
17 | import com.paypal.checkout.order.OrderRequest
18 | import com.paypal.checkout.order.PurchaseUnit
19 | import com.paypal.checkout.paymentbutton.PaymentButtonEligibilityStatus
20 | import com.paypal.checkoutsamples.R
21 | import kotlinx.android.synthetic.main.activity_payment_button_quick_start.*
22 |
23 | class PaymentButtonQuickStartActivity : AppCompatActivity() {
24 |
25 | private val tag = javaClass.simpleName
26 |
27 | override fun onCreate(savedInstanceState: Bundle?) {
28 | super.onCreate(savedInstanceState)
29 | setContentView(R.layout.activity_payment_button_quick_start)
30 | paymentButton.onEligibilityStatusChanged = { buttonEligibilityStatus: PaymentButtonEligibilityStatus ->
31 | Log.v(tag, "OnEligibilityStatusChanged")
32 | Log.d(tag, "Button eligibility status: $buttonEligibilityStatus")
33 | }
34 | setupPaymentButton()
35 | }
36 |
37 | private fun setupPaymentButton() {
38 | paymentButton.setup(
39 | createOrder = CreateOrder { createOrderActions ->
40 | Log.v(tag, "CreateOrder")
41 | createOrderActions.create(
42 | OrderRequest.Builder()
43 | .appContext(
44 | AppContext(
45 | userAction = UserAction.PAY_NOW
46 | )
47 | )
48 | .intent(OrderIntent.CAPTURE)
49 | .purchaseUnitList(
50 | listOf(
51 | PurchaseUnit.Builder()
52 | .amount(
53 | Amount.Builder()
54 | .value("0.01")
55 | .currencyCode(CurrencyCode.USD)
56 | .build()
57 | )
58 | .build()
59 | )
60 | )
61 | .build()
62 | .also { Log.d(tag, "Order: $it") }
63 | )
64 | },
65 | onApprove = OnApprove { approval ->
66 | Log.v(tag, "OnApprove")
67 | Log.d(tag, "Approval details: $approval")
68 | approval.orderActions.capture { captureOrderResult ->
69 | Log.v(tag, "Capture Order")
70 | Log.d(tag, "Capture order result: $captureOrderResult")
71 | }
72 | },
73 | onCancel = OnCancel {
74 | Log.v(tag, "OnCancel")
75 | Log.d(tag, "Buyer cancelled the checkout experience.")
76 | },
77 | onError = OnError { errorInfo ->
78 | Log.v(tag, "OnError")
79 | Log.d(tag, "Error details: $errorInfo")
80 | },
81 | // This sample app has not obtained consent from the buyer to collect location data.
82 | // This flag enables PayPal to collect necessary information required for Fraud Detection and Risk Management
83 | hasUserLocationConsent = false
84 | )
85 | }
86 |
87 | companion object {
88 | fun startIntent(context: Context): Intent {
89 | return Intent(context, PaymentButtonQuickStartActivity::class.java)
90 | }
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/quickstart-kotlin/src/main/java/com/paypal/checkoutsamples/token/TokenQuickStartActivity.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.checkoutsamples.token
2 |
3 | import android.content.Context
4 | import android.content.Intent
5 | import android.os.Bundle
6 | import android.util.Log
7 | import androidx.appcompat.app.AppCompatActivity
8 | import androidx.core.widget.addTextChangedListener
9 | import com.paypal.checkout.PayPalCheckout
10 | import com.paypal.checkout.createorder.CreateOrder
11 | import com.paypal.checkout.createorder.CurrencyCode
12 | import com.paypal.checkout.createorder.OrderIntent
13 | import com.paypal.checkout.createorder.UserAction
14 | import com.paypal.checkoutsamples.R
15 | import com.paypal.checkoutsamples.token.repository.CheckoutApi
16 | import com.paypal.checkoutsamples.token.repository.CreatedOrder
17 | import com.paypal.checkoutsamples.token.repository.OrderRepository
18 | import com.paypal.checkoutsamples.token.repository.request.AmountRequest
19 | import com.paypal.checkoutsamples.token.repository.request.ApplicationContextRequest
20 | import com.paypal.checkoutsamples.token.repository.request.OrderRequest
21 | import com.paypal.checkoutsamples.token.repository.request.PurchaseUnitRequest
22 | import kotlinx.android.synthetic.main.activity_token_quick_start.*
23 | import kotlinx.coroutines.MainScope
24 | import kotlinx.coroutines.cancelChildren
25 | import kotlinx.coroutines.launch
26 | import java.io.IOException
27 |
28 | class TokenQuickStartActivity : AppCompatActivity() {
29 |
30 | private val tag = this::class.java.toString()
31 |
32 | private val checkoutApi = CheckoutApi()
33 |
34 | private val orderRepository = OrderRepository(checkoutApi)
35 |
36 | private val checkoutSdk: PayPalCheckout
37 | get() = PayPalCheckout
38 |
39 | private val selectedUserAction: UserAction
40 | get() {
41 | return when (val selectedId = selectUserAction.checkedRadioButtonId) {
42 | R.id.userActionOptionContinue -> UserAction.CONTINUE
43 | R.id.userActionOptionPayNow -> UserAction.PAY_NOW
44 | else -> {
45 | throw IllegalArgumentException(
46 | "Expected one of the following ids: ${R.id.userActionOptionContinue}, or " +
47 | "${R.id.userActionOptionPayNow} but was $selectedId"
48 | )
49 | }
50 | }
51 | }
52 |
53 | private val selectedOrderIntent: OrderIntent
54 | get() {
55 | return when (val selectedId = selectOrderIntent.checkedRadioButtonId) {
56 | R.id.orderIntentOptionAuthorize -> OrderIntent.AUTHORIZE
57 | R.id.orderIntentOptionCapture -> OrderIntent.CAPTURE
58 | else -> {
59 | throw IllegalArgumentException(
60 | "Expected one of the following ids: ${R.id.orderIntentOptionAuthorize}, or " +
61 | "${R.id.orderIntentOptionCapture} but was $selectedId"
62 | )
63 | }
64 | }
65 | }
66 |
67 | private val selectedCurrencyCode: CurrencyCode
68 | get() {
69 | return when (val selectedId = selectCurrencyCode.checkedRadioButtonId) {
70 | R.id.currencyCodeUsd -> CurrencyCode.USD
71 | R.id.currencyCodeEur -> CurrencyCode.EUR
72 | R.id.currencyCodeGbp -> CurrencyCode.GBP
73 | else -> {
74 | throw IllegalArgumentException(
75 | "Expected one of the following ids: ${R.id.currencyCodeUsd}, " +
76 | "${R.id.currencyCodeEur}, or ${R.id.currencyCodeGbp} but was $selectedId"
77 | )
78 | }
79 | }
80 | }
81 |
82 | private val enteredAmount: String
83 | get() = totalAmountInput.editText!!.text.toString()
84 |
85 | private val uiScope = MainScope()
86 |
87 | override fun onCreate(savedInstanceState: Bundle?) {
88 | super.onCreate(savedInstanceState)
89 | setContentView(R.layout.activity_token_quick_start)
90 |
91 | totalAmountInput.editText?.addTextChangedListener { totalAmountInput.error = null }
92 |
93 | submitTokenButton.setOnClickListener {
94 | if (totalAmountInput.editText!!.text.isEmpty()) {
95 | totalAmountInput.error = getString(R.string.token_quick_start_activity_total_amount_required)
96 | return@setOnClickListener
97 | }
98 |
99 | startCheckout()
100 | }
101 | }
102 |
103 | private fun startCheckout() {
104 | checkoutSdk.startCheckout(
105 | createOrder = CreateOrder { createOrderActions ->
106 | uiScope.launch {
107 | val createdOrder = createOrder()
108 | createdOrder?.let { createOrderActions.set(createdOrder.id) }
109 | }
110 | },
111 | // This sample app has not obtained consent from the buyer to collect location data.
112 | // This flag enables PayPal to collect necessary information required for Fraud Detection and Risk Management
113 | hasUserLocationConsent = false
114 | )
115 | }
116 |
117 | private suspend fun createOrder(): CreatedOrder? {
118 | val orderRequest = createOrderRequest()
119 | return try {
120 | orderRepository.create(orderRequest)
121 | } catch (ex: IOException) {
122 | Log.w(tag, "Attempt to create order failed with the following message: ${ex.message}")
123 | null
124 | }
125 | }
126 |
127 | private fun createOrderRequest(): OrderRequest {
128 | return OrderRequest(
129 | intent = selectedOrderIntent.name,
130 | applicationContext = ApplicationContextRequest(
131 | userAction = selectedUserAction.name
132 | ),
133 | purchaseUnits = listOf(
134 | PurchaseUnitRequest(
135 | amount = AmountRequest(
136 | value = enteredAmount,
137 | currencyCode = selectedCurrencyCode.name
138 | )
139 | )
140 | )
141 | ).also { Log.i(tag, "OrderRequest: $it") }
142 | }
143 |
144 | override fun onStop() {
145 | super.onStop()
146 | uiScope.coroutineContext.cancelChildren()
147 | }
148 |
149 | companion object {
150 | fun startIntent(context: Context): Intent {
151 | return Intent(context, TokenQuickStartActivity::class.java)
152 | }
153 | }
154 | }
155 |
--------------------------------------------------------------------------------
/quickstart-kotlin/src/main/java/com/paypal/checkoutsamples/token/repository/AuthTokenRepository.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.checkoutsamples.token.repository
2 |
3 | import android.util.Log
4 | import kotlinx.coroutines.CoroutineDispatcher
5 | import kotlinx.coroutines.Dispatchers
6 | import kotlinx.coroutines.withContext
7 | import java.util.Date
8 |
9 | /**
10 | * AuthTokenRepository provides a way to retrieve a valid [AuthToken]. If you encounter errors with
11 | * this repository please ensure you have a valid client id and secret set in QuickStartConstants.
12 | */
13 | class AuthTokenRepository(
14 | private val checkoutApi: CheckoutApi,
15 | private val dispatcher: CoroutineDispatcher = Dispatchers.IO
16 | ) {
17 | private val tag = this::class.java.toString()
18 |
19 | /**
20 | * Retrieves a valid [AuthToken]. If one does not exist or has expired a new one will be created
21 | * and returned.
22 | */
23 | suspend fun retrieve(): AuthToken {
24 | val currentAuthToken = authToken
25 | return if (currentAuthToken == null || currentAuthToken.expiresAt < Date()) {
26 | Log.i(tag, "Creating a new OAuth Token...")
27 | val oAuthToken = withContext(dispatcher) {
28 | checkoutApi.postOAuthToken()
29 | }
30 | Log.i(tag, "New token created...")
31 | Log.d(tag, "Token: $oAuthToken")
32 |
33 | val expiresAt: Long = (oAuthToken.expiresIn * expirationFactor).toLong()
34 | AuthToken(
35 | accessToken = oAuthToken.accessToken,
36 | expiresAt = Date().add(expiresAt)
37 | )
38 | .also { authToken = it }
39 | .also { Log.d(tag, "New token cached: $it") }
40 | } else {
41 | Log.i(tag, "Valid token exists, returning to caller.")
42 | currentAuthToken
43 | }
44 | }
45 |
46 | private fun Date.add(seconds: Long): Date = apply { time += seconds }
47 |
48 | companion object {
49 | /**
50 | * The expirationFactor is applied to our OAuth Token expiration time. The primary goal is
51 | * to avoid using a token just as it's expiring, so instead of using the full time returned
52 | * by the API we only use 95% of that time to avoid making requests with an expired token.
53 | */
54 | private const val expirationFactor = 0.95
55 |
56 | private var authToken: AuthToken? = null
57 | }
58 | }
59 |
60 | data class AuthToken(
61 | val accessToken: String,
62 | val expiresAt: Date
63 | )
64 |
--------------------------------------------------------------------------------
/quickstart-kotlin/src/main/java/com/paypal/checkoutsamples/token/repository/CheckoutApi.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.checkoutsamples.token.repository
2 |
3 | import com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFactory
4 | import com.paypal.checkoutsamples.PAYPAL_CLIENT_ID
5 | import com.paypal.checkoutsamples.PAYPAL_SECRET
6 | import com.paypal.checkoutsamples.token.repository.request.OrderRequest
7 | import com.paypal.checkoutsamples.token.repository.response.OAuthTokenResponse
8 | import com.paypal.checkoutsamples.token.repository.response.OrderResponse
9 | import kotlinx.serialization.ExperimentalSerializationApi
10 | import kotlinx.serialization.json.Json
11 | import okhttp3.Credentials
12 | import okhttp3.MediaType.Companion.toMediaType
13 | import retrofit2.Retrofit
14 | import retrofit2.http.Body
15 | import retrofit2.http.Field
16 | import retrofit2.http.FormUrlEncoded
17 | import retrofit2.http.Header
18 | import retrofit2.http.Headers
19 | import retrofit2.http.POST
20 |
21 | /**
22 | * CheckoutApi includes the necessary endpoints for creating an order and receiving an EC token that
23 | * can be used for launching the Checkout SDK pay sheet with a token.
24 | *
25 | * Note: when implementing your own integration, you should avoid creating the OAuth Token within your
26 | * Android app as the creating an OAuth Token in these examples require passing your app secret.
27 | */
28 | interface CheckoutApi {
29 |
30 | /**
31 | * Creates a new OAuth Token that can be used for subsequent API requests.
32 | *
33 | * @see payPalAuthorization
34 | */
35 | @Headers("Accept: application/json", "Accept-Language: en_US")
36 | @FormUrlEncoded
37 | @POST("/v1/oauth2/token")
38 | suspend fun postOAuthToken(
39 | @Header("Authorization") authorization: String = payPalAuthorization,
40 | @Field("grant_type") grantType: String = "client_credentials"
41 | ): OAuthTokenResponse
42 |
43 | /**
44 | * Creates a new Order, the subsequent id ([OrderResponse.id]) can then be used to start a new
45 | * pay sheet instance.
46 | */
47 | @Headers("Accept: application/json")
48 | @POST("/v2/checkout/orders")
49 | suspend fun postCheckoutOrder(
50 | @Header("Authorization") authorization: String,
51 | @Body orderRequest: OrderRequest
52 | ): OrderResponse
53 |
54 | companion object {
55 |
56 | /**
57 | * Valid credentials for creating a server <-> server auth token require a valid client id
58 | * as the user and a corresponding secret for the password. This should be encoded in Base64.
59 | *
60 | * The proper format prior to encoding is as follows: clientId:secret
61 | *
62 | * OkHttp provides a convenient "Basic" function which handles the heavy lifting for us.
63 | */
64 | val payPalAuthorization: String = Credentials.basic(PAYPAL_CLIENT_ID, PAYPAL_SECRET)
65 |
66 | /**
67 | * Provides an easy way to instantiate a [CheckoutApi] as you normally would if this were a
68 | * class instead of an interface.
69 | *
70 | * Example: val checkoutApi = CheckoutApi()
71 | */
72 | @ExperimentalSerializationApi
73 | operator fun invoke(): CheckoutApi {
74 | val json = Json {
75 | ignoreUnknownKeys = true
76 | }
77 | val retrofit = Retrofit.Builder()
78 | .baseUrl("https://api.sandbox.paypal.com/")
79 | .addConverterFactory(
80 | json.asConverterFactory("application/json".toMediaType())
81 | )
82 | .build()
83 |
84 | return retrofit.create(CheckoutApi::class.java)
85 | }
86 | }
87 | }
88 |
89 | /**
90 | * Updates a [String] to be a valid Bearer token (assuming the String itself is a valid OAuth Token).
91 | */
92 | val String.asBearer: String
93 | get() = "Bearer $this"
94 |
--------------------------------------------------------------------------------
/quickstart-kotlin/src/main/java/com/paypal/checkoutsamples/token/repository/OrderRepository.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.checkoutsamples.token.repository
2 |
3 | import android.util.Log
4 | import com.paypal.checkoutsamples.token.repository.request.OrderRequest
5 | import com.paypal.checkoutsamples.token.repository.response.OrderResponse
6 | import kotlinx.coroutines.CoroutineDispatcher
7 | import kotlinx.coroutines.Dispatchers
8 | import kotlinx.coroutines.withContext
9 | import retrofit2.HttpException
10 |
11 | /**
12 | * OrderRepository provides an entry point to create new orders. The main purpose with regards to
13 | * this sample app is being able to get an Order ID (EC Token) which can then be provided to the
14 | * Checkout SDK to launch a pay sheet.
15 | */
16 | class OrderRepository(
17 | private val checkoutApi: CheckoutApi,
18 | private val authTokenRepository: AuthTokenRepository = AuthTokenRepository(checkoutApi),
19 | private val dispatcher: CoroutineDispatcher = Dispatchers.IO
20 | ) {
21 | private val tag = javaClass.simpleName
22 |
23 | /**
24 | * Creates a new Order ([CreatedOrder]) given a [CreateOrderRequest].
25 | */
26 | suspend fun create(request: CreateOrderRequest): CreatedOrder {
27 | Log.d(tag, "Create order request: $request")
28 | val token = authTokenRepository.retrieve()
29 | return try {
30 | withContext(dispatcher) {
31 | checkoutApi.postCheckoutOrder(
32 | authorization = token.accessToken.asBearer,
33 | orderRequest = request
34 | )
35 | }
36 | } catch (ex: HttpException) {
37 | Log.w(tag, "Unable to create order with token: $token")
38 | Log.e(tag, "Could not create order: $ex")
39 | throw ex
40 | }
41 | }
42 | }
43 |
44 | typealias CreateOrderRequest = OrderRequest
45 | typealias CreatedOrder = OrderResponse
46 |
--------------------------------------------------------------------------------
/quickstart-kotlin/src/main/java/com/paypal/checkoutsamples/token/repository/request/OrderRequest.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.checkoutsamples.token.repository.request
2 |
3 | import com.paypal.checkout.createorder.CurrencyCode
4 | import kotlinx.serialization.SerialName
5 | import kotlinx.serialization.Serializable
6 |
7 | /**
8 | * OrderRequest is used for creating a new Order with the v2 Order's API.
9 | *
10 | * @see https://developer.paypal.com/docs/api/orders/v2/#definition-order_request
11 | *
12 | * @property intent describes how the order should be handled, should it be captured so the checkout is
13 | * completely shortly after the pay sheet is complete or authorized.
14 | * @property applicationContext provides details about application being used for placing the order, of
15 | * note is the user action which determines whether or not we show a cart total.
16 | * @property purchaseUnits contains details about the items that are part of the order.
17 | */
18 | @Serializable
19 | data class OrderRequest(
20 | @SerialName("intent")
21 | val intent: String,
22 | @SerialName("application_context")
23 | val applicationContext: ApplicationContextRequest,
24 | @SerialName("purchase_units")
25 | val purchaseUnits: List
26 | )
27 |
28 | /**
29 | * ApplicationContextRequest provides additional details about the application. For the purpose of this
30 | * sample we are only concerned with a subset of the parameters.
31 | *
32 | * @see https://developer.paypal.com/docs/api/orders/v2/#definition-order_application_context
33 | *
34 | * @property userAction determines whether or not the pay sheet will display the total order amount via
35 | * PAY_NOW being passed in. When CONTINUE is provided then the pay sheet will not display the total.
36 | */
37 | @Serializable
38 | data class ApplicationContextRequest(
39 | @SerialName("user_action")
40 | val userAction: String,
41 | )
42 |
43 | /**
44 | * PurchaseUnitRequest is used to provide item, payment, and shipping information. For the purpose of
45 | * this sample we are only concerned with a subset of the available parameters.
46 | *
47 | * @see https://developer.paypal.com/docs/api/orders/v2/#definition-purchase_unit_request
48 | *
49 | * @param amount is the total amount for the order.
50 | */
51 | @Serializable
52 | data class PurchaseUnitRequest(
53 | @SerialName("amount")
54 | val amount: AmountRequest,
55 | )
56 |
57 | /**
58 | * AmountRequest is used for outlining the amount of something (item, shipping, total, etc).
59 | *
60 | * @see https://developer.paypal.com/docs/api/orders/v2/#definition-order_request
61 | * @see [CurrencyCode]
62 | *
63 | * @property currencyCode defines what currency is being used for this order.
64 | * @property value defines how much of the amount is.
65 | *
66 | * Example, value = 100 + currencyCode = USD is how you would represent $100
67 | */
68 | @Serializable
69 | data class AmountRequest(
70 | @SerialName("currency_code")
71 | val currencyCode: String,
72 | @SerialName("value")
73 | val value: String = "0.01"
74 | )
75 |
--------------------------------------------------------------------------------
/quickstart-kotlin/src/main/java/com/paypal/checkoutsamples/token/repository/response/OAuthTokenResponse.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.checkoutsamples.token.repository.response
2 |
3 | import kotlinx.serialization.SerialName
4 | import kotlinx.serialization.Serializable
5 |
6 | /**
7 | * OAuthTokenResponse is used for capturing an OAuth Token and the params available here are a subset
8 | * of the ones actually returned.
9 | *
10 | * @property accessToken should be used for other API requests, passing it in as "Bearer [accessToken]".
11 | * @property expiresIn provides the amount of time this token will be valid for to make it easier to
12 | * know when to re-authenticate.
13 | */
14 | @Serializable
15 | data class OAuthTokenResponse(
16 | @SerialName("access_token")
17 | val accessToken: String,
18 | @SerialName("expires_in")
19 | val expiresIn: Long
20 | )
21 |
--------------------------------------------------------------------------------
/quickstart-kotlin/src/main/java/com/paypal/checkoutsamples/token/repository/response/OrderResponse.kt:
--------------------------------------------------------------------------------
1 | package com.paypal.checkoutsamples.token.repository.response
2 |
3 | import kotlinx.serialization.Serializable
4 |
5 | /**
6 | * OrderResponse is a partial response returned by /v2/checkout/orders
7 | *
8 | * @property id is the identifier for the Order, it is also referred to as a token or ec token. This
9 | * is used when creating a new pay sheet via the Checkout SDK.
10 | */
11 | @Serializable
12 | data class OrderResponse(
13 | val id: String
14 | )
15 |
--------------------------------------------------------------------------------
/quickstart-kotlin/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
15 |
18 |
21 |
22 |
23 |
24 |
30 |
--------------------------------------------------------------------------------
/quickstart-kotlin/src/main/res/drawable/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
10 |
15 |
20 |
25 |
30 |
35 |
40 |
45 |
50 |
55 |
60 |
65 |
70 |
75 |
80 |
85 |
90 |
95 |
100 |
105 |
110 |
115 |
120 |
125 |
130 |
135 |
140 |
145 |
150 |
155 |
160 |
165 |
170 |
171 |
--------------------------------------------------------------------------------
/quickstart-kotlin/src/main/res/layout/activity_kotlin_quick_start.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
20 |
21 |
30 |
31 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/quickstart-kotlin/src/main/res/layout/activity_orders_quick_start.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
14 |
15 |
24 |
25 |
34 |
35 |
40 |
41 |
47 |
48 |
54 |
55 |
56 |
57 |
66 |
67 |
72 |
73 |
79 |
80 |
86 |
87 |
88 |
89 |
98 |
99 |
104 |
105 |
111 |
112 |
118 |
119 |
125 |
126 |
127 |
128 |
134 |
135 |
145 |
146 |
155 |
156 |
164 |
165 |
173 |
174 |
180 |
181 |
189 |
190 |
199 |
200 |
205 |
206 |
212 |
213 |
219 |
220 |
226 |
227 |
228 |
229 |
238 |
239 |
247 |
248 |
249 |
250 |
251 |
252 |
--------------------------------------------------------------------------------
/quickstart-kotlin/src/main/res/layout/activity_payment_button_quick_start.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
14 |
15 |
24 |
25 |
34 |
35 |
43 |
44 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/quickstart-kotlin/src/main/res/layout/activity_token_quick_start.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
19 |
20 |
29 |
30 |
35 |
36 |
42 |
43 |
49 |
50 |
51 |
52 |
61 |
62 |
67 |
68 |
74 |
75 |
81 |
82 |
83 |
84 |
93 |
94 |
99 |
100 |
106 |
107 |
113 |
114 |
120 |
121 |
122 |
123 |
132 |
133 |
137 |
138 |
139 |
140 |
148 |
149 |
--------------------------------------------------------------------------------
/quickstart-kotlin/src/main/res/layout/dialog_create_item.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
12 |
13 |
22 |
23 |
31 |
32 |
35 |
36 |
37 |
38 |
48 |
49 |
53 |
54 |
55 |
56 |
67 |
68 |
72 |
73 |
74 |
75 |
85 |
86 |
90 |
91 |
92 |
93 |
99 |
100 |
110 |
111 |
116 |
117 |
123 |
124 |
130 |
131 |
132 |
133 |
140 |
141 |
150 |
151 |
152 |
153 |
154 |
--------------------------------------------------------------------------------
/quickstart-kotlin/src/main/res/layout/item_preview_item.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
19 |
20 |
26 |
27 |
33 |
34 |
43 |
44 |
53 |
54 |
63 |
64 |
--------------------------------------------------------------------------------
/quickstart-kotlin/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/quickstart-kotlin/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/quickstart-kotlin/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/paypal/android-checkout-sdk/991dd4c462bfc954c29e1b8528d2e4ae043ee99f/quickstart-kotlin/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/quickstart-kotlin/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/paypal/android-checkout-sdk/991dd4c462bfc954c29e1b8528d2e4ae043ee99f/quickstart-kotlin/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/quickstart-kotlin/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/paypal/android-checkout-sdk/991dd4c462bfc954c29e1b8528d2e4ae043ee99f/quickstart-kotlin/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/quickstart-kotlin/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/paypal/android-checkout-sdk/991dd4c462bfc954c29e1b8528d2e4ae043ee99f/quickstart-kotlin/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/quickstart-kotlin/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/paypal/android-checkout-sdk/991dd4c462bfc954c29e1b8528d2e4ae043ee99f/quickstart-kotlin/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/quickstart-kotlin/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/paypal/android-checkout-sdk/991dd4c462bfc954c29e1b8528d2e4ae043ee99f/quickstart-kotlin/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/quickstart-kotlin/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/paypal/android-checkout-sdk/991dd4c462bfc954c29e1b8528d2e4ae043ee99f/quickstart-kotlin/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/quickstart-kotlin/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/paypal/android-checkout-sdk/991dd4c462bfc954c29e1b8528d2e4ae043ee99f/quickstart-kotlin/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/quickstart-kotlin/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/paypal/android-checkout-sdk/991dd4c462bfc954c29e1b8528d2e4ae043ee99f/quickstart-kotlin/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/quickstart-kotlin/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/paypal/android-checkout-sdk/991dd4c462bfc954c29e1b8528d2e4ae043ee99f/quickstart-kotlin/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/quickstart-kotlin/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #6200EE
4 | #3700B3
5 | #03DAC5
6 |
--------------------------------------------------------------------------------
/quickstart-kotlin/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 16dp
4 | 16dp
5 | 16dp
6 | 8dp
7 | 8dp
8 |
--------------------------------------------------------------------------------
/quickstart-kotlin/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | Android Native Checkout Samples
3 |
4 | Client Side Only Sample
5 | Server Side Sample
6 | Payment Button Sample
7 |
8 | Orders Quick Start
9 | Configure Client Side Integration
10 | User Action:
11 | Continue
12 | Pay Now
13 | Order Intent:
14 | Capture
15 | Authorize
16 | Currency Code:
17 | USD
18 | EUR
19 | GBP
20 | Add Items To Your Order
21 | +
22 | At least one item is required
23 | Shipping Preferences
24 | Shipping Preference:
25 | Get From File
26 | No Shipping
27 | Set Provided Address
28 | Enable Shipping Callbacks?
29 | Place Order
30 | x%1$s
31 |
32 | Payment Button
33 | Payment Button
34 | To get a better understanding for how Payment Buttons work within the Checkout SDK, please observe the logs which are printed in Logcat, filtering on PaymentButtonQuickStartActivity.\n\nTo see other buttons or customize this one, please view the activity_payment_button_quick_start layout file.
35 |
36 | Configure Server Side Integration
37 | @string/orders_quick_start_activity_configuration_user_action_label
38 | @string/orders_quick_start_activity_configuration_user_action_option_continue
39 | @string/orders_quick_start_activity_configuration_user_action_option_pay_now
40 | @string/orders_quick_start_activity_configuration_intent_label
41 | @string/orders_quick_start_activity_configuration_intent_option_capture
42 | @string/orders_quick_start_activity_configuration_intent_option_authorize
43 | Total Amount
44 | Total Amount Required
45 | @string/orders_quick_start_activity_submit_order_button
46 |
47 | Create An Item
48 | Item Name
49 | Quantity
50 | Amount
51 | Tax Amount
52 | Item Category:
53 | Physical Goods
54 | Digital Goods
55 | Create Item
56 | Item Category is required
57 | Required
58 |
59 |
60 |
--------------------------------------------------------------------------------
/quickstart-kotlin/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
11 |
12 |
13 |
14 |
18 |
19 |
22 |
23 |
26 |
27 |
30 |
31 |
35 |
36 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':quickstart-kotlin'
2 | rootProject.name = "Android Native Checkout Samples"
--------------------------------------------------------------------------------