├── .gitignore ├── .idea └── codeStyles │ ├── Project.xml │ └── codeStyleConfig.xml ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro ├── release │ └── output-metadata.json └── src │ └── main │ ├── AndroidManifest.xml │ ├── ic_launcher-playstore.png │ ├── java │ └── ru │ │ └── cloudpayments │ │ └── demo │ │ ├── Constants.kt │ │ ├── api │ │ └── PayApi.kt │ │ ├── base │ │ ├── BaseActivity.kt │ │ ├── BaseAdapter.kt │ │ └── BaseListActivity.kt │ │ ├── googlepay │ │ ├── ConstantsGPay.kt │ │ └── PaymentsUtil.kt │ │ ├── managers │ │ └── CartManager.kt │ │ ├── models │ │ └── Product.kt │ │ ├── screens │ │ ├── checkout │ │ │ └── CheckoutActivity.kt │ │ └── main │ │ │ └── MainActivity.kt │ │ └── support │ │ ├── CardIOScanner.kt │ │ ├── Constants.kt │ │ └── SideSpaceItemDecoration.kt │ └── res │ ├── drawable-v24 │ └── ic_launcher_foreground.xml │ ├── drawable-xxhdpi │ ├── logo.png │ ├── product.png │ └── splash_logo.png │ ├── drawable │ ├── googlepay_button_content.xml │ ├── googlepay_button_overlay.xml │ ├── ic_credit_card_black_24dp.xml │ ├── ic_date_range_black_24dp.xml │ ├── ic_email_white_24dp.xml │ ├── ic_launcher_foreground.xml │ ├── ic_lock_black_24dp.xml │ ├── ic_person_black_24dp.xml │ ├── ic_phone_white_24dp.xml │ ├── ic_shopping_cart_white_24dp.xml │ └── splashscreen_bg.xml │ ├── layout │ ├── activity_checkout.xml │ ├── activity_main.xml │ ├── googlepay_button.xml │ └── toolbar.xml │ ├── menu │ └── main.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 │ ├── ic_launcher_background.xml │ ├── strings.xml │ └── styles.xml ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── local.properties ├── sdk ├── .gitignore ├── build.gradle ├── consumer-rules.pro ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── ru │ │ └── cloudpayments │ │ └── sdk │ │ ├── Constants.kt │ │ ├── api │ │ ├── AuthenticationInterceptor.kt │ │ ├── CloudpaymentsApi.kt │ │ ├── CloudpaymentsApiService.kt │ │ └── models │ │ │ ├── CardCryptogramPacket.kt │ │ │ ├── CloudpaymentsBinInfoResponse.kt │ │ │ ├── CloudpaymentsGetTinkoffPayQrLinkResponse.kt │ │ │ ├── CloudpaymentsMerchantConfigurationResponse.kt │ │ │ ├── CloudpaymentsPublicKeyResponse.kt │ │ │ ├── CloudpaymentsThreeDsResponse.kt │ │ │ ├── CloudpaymentsTinkoffPayQrLinkTransaction.kt │ │ │ ├── CloudpaymentsTransaction.kt │ │ │ ├── CloudpaymentsTransactionError.kt │ │ │ ├── CloudpaymentsTransactionResponse.kt │ │ │ ├── PaymentDataPayer.kt │ │ │ ├── PaymentRequestBody.kt │ │ │ ├── QrLinkStatusWait.kt │ │ │ ├── QrLinkStatusWaitBody.kt │ │ │ ├── QrLinkStatusWaitResponse.kt │ │ │ ├── ThreeDsMdData.kt │ │ │ ├── ThreeDsRequestBody.kt │ │ │ └── TinkoffPayQrLinkBody.kt │ │ ├── card │ │ ├── Card.kt │ │ └── CardType.kt │ │ ├── configuration │ │ ├── CloudPaymentsIntentSender.kt │ │ ├── CloudpaymentsSDK.kt │ │ ├── PaymentConfiguration.kt │ │ └── PaymentData.kt │ │ ├── dagger2 │ │ └── CloudpaymentsModule.kt │ │ ├── models │ │ ├── ApiError.kt │ │ ├── Currency.kt │ │ ├── PayParams.kt │ │ └── Transaction.kt │ │ ├── scanner │ │ ├── CardData.kt │ │ └── CardScanner.kt │ │ ├── ui │ │ ├── PaymentActivity.kt │ │ └── dialogs │ │ │ ├── PaymentCardFragment.kt │ │ │ ├── PaymentOptionsFragment.kt │ │ │ ├── PaymentProcessFragment.kt │ │ │ ├── ThreeDsDialogFragment.kt │ │ │ └── base │ │ │ ├── BasePaymentBottomSheetFragment.kt │ │ │ ├── BasePaymentDialogFragment.kt │ │ │ ├── BaseVMBottomSheetFragment.kt │ │ │ └── BaseVMDialogFragment.kt │ │ ├── util │ │ ├── Constants.kt │ │ ├── Extensions.kt │ │ ├── GooglePayHandler.kt │ │ ├── HexPacketHelper.kt │ │ ├── InjectorUtils.kt │ │ ├── PublicKey.kt │ │ └── Tools.kt │ │ └── viewmodel │ │ ├── BaseViewModel.kt │ │ ├── BaseViewState.kt │ │ ├── PaymentCardViewModel.kt │ │ ├── PaymentOptionsViewModel.kt │ │ ├── PaymentProcessViewModel.kt │ │ └── PaymentProcessViewModelFactory.kt │ └── res │ ├── anim │ ├── cpsdk_fade_in.xml │ ├── cpsdk_fade_out.xml │ ├── cpsdk_slide_in.xml │ └── cpsdk_slide_out.xml │ ├── color │ └── cpsdk_color_edittext.xml │ ├── drawable-hdpi │ ├── cpsdk_button_tinkoff_pay.png │ ├── cpsdk_ic_checkbox_checked.png │ ├── cpsdk_ic_checkbox_unchecked.png │ ├── cpsdk_ic_failure.png │ ├── cpsdk_ic_progress.png │ ├── cpsdk_ic_ps_jcb.png │ ├── cpsdk_ic_ps_mir.png │ ├── cpsdk_ic_success.png │ ├── drag_handle.png │ └── ic_required_email.png │ ├── drawable-mdpi │ ├── cpsdk_button_tinkoff_pay.png │ ├── cpsdk_ic_checkbox_unchecked.png │ ├── cpsdk_ic_failure.png │ ├── cpsdk_ic_progress.png │ ├── cpsdk_ic_ps_jcb.png │ ├── cpsdk_ic_ps_mir.png │ ├── cpsdk_ic_success.png │ ├── drag_handle.png │ └── ic_required_email.png │ ├── drawable-v21 │ └── cpsdk_bg_button_gp.xml │ ├── drawable-xhdpi │ ├── cpsdk_button_tinkoff_pay.png │ ├── cpsdk_ic_checkbox_checked.png │ ├── cpsdk_ic_checkbox_unchecked.png │ ├── cpsdk_ic_failure.png │ ├── cpsdk_ic_progress.png │ ├── cpsdk_ic_ps_jcb.png │ ├── cpsdk_ic_ps_mir.png │ ├── cpsdk_ic_success.png │ ├── drag_handle.png │ └── ic_required_email.png │ ├── drawable-xxhdpi │ ├── cpsdk_button_tinkoff_pay.png │ ├── cpsdk_ic_checkbox_checked.png │ ├── cpsdk_ic_checkbox_unchecked.png │ ├── cpsdk_ic_failure.png │ ├── cpsdk_ic_progress.png │ ├── cpsdk_ic_ps_jcb.png │ ├── cpsdk_ic_ps_mir.png │ ├── cpsdk_ic_success.png │ ├── drag_handle.png │ └── ic_required_email.png │ ├── drawable-xxxhdpi │ ├── cpsdk_button_tinkoff_pay.png │ ├── cpsdk_ic_checkbox_checked.png │ ├── cpsdk_ic_checkbox_unchecked.png │ ├── cpsdk_ic_failure.png │ ├── cpsdk_ic_progress.png │ ├── cpsdk_ic_ps_jcb.png │ ├── cpsdk_ic_ps_mir.png │ ├── cpsdk_ic_success.png │ ├── drag_handle.png │ └── ic_required_email.png │ ├── drawable │ ├── cpsdk_bg_button_close.xml │ ├── cpsdk_bg_button_gp.xml │ ├── cpsdk_bg_button_gp_black.xml │ ├── cpsdk_bg_edit_text.xml │ ├── cpsdk_bg_edit_text_error.xml │ ├── cpsdk_bg_edit_text_focused.xml │ ├── cpsdk_bg_edit_text_selector.xml │ ├── cpsdk_bg_edit_text_selector_error.xml │ ├── cpsdk_bg_popup.xml │ ├── cpsdk_bg_rounded_black_button.xml │ ├── cpsdk_bg_rounded_blue_button.xml │ ├── cpsdk_bg_rounded_bottom_sheet.xml │ ├── cpsdk_bg_rounded_dialog.xml │ ├── cpsdk_bg_rounded_dialog_inset.xml │ ├── cpsdk_bg_rounded_white_button_with_border.xml │ ├── cpsdk_edit_text_underline.xml │ ├── cpsdk_edit_text_underline_error.xml │ ├── cpsdk_googlepay_button_content.xml │ ├── cpsdk_googlepay_button_overlay.xml │ ├── cpsdk_ic_blue_circle.xml │ ├── cpsdk_ic_checkbox_selector.xml │ ├── cpsdk_ic_close.xml │ ├── cpsdk_ic_ps_amex.xml │ ├── cpsdk_ic_ps_maestro.xml │ ├── cpsdk_ic_ps_mastercard.xml │ ├── cpsdk_ic_ps_troy.xml │ ├── cpsdk_ic_ps_visa.xml │ ├── cpsdk_ic_save_card_popup.xml │ ├── cpsdk_ic_scan.xml │ ├── cpsdk_ic_secured.xml │ └── cpsdk_secured_logo.xml │ ├── font │ ├── rubik.xml │ ├── rubik_black.ttf │ ├── rubik_black_italic.ttf │ ├── rubik_bold.ttf │ ├── rubik_bold_italic.ttf │ ├── rubik_italic.ttf │ ├── rubik_light.ttf │ ├── rubik_light_italic.ttf │ ├── rubik_medium.ttf │ ├── rubik_medium_italic.ttf │ ├── rubik_regular.ttf │ └── sf_pro_text_regular.ttf │ ├── layout │ ├── activity_cpsdk_payment.xml │ ├── dialog_cpsdk_payment_card.xml │ ├── dialog_cpsdk_payment_options.xml │ ├── dialog_cpsdk_payment_process.xml │ ├── dialog_cpsdk_three_ds.xml │ ├── googlepay_button.xml │ └── popup_cpsdk_save_card_info.xml │ ├── values-v21 │ └── styles.xml │ └── values │ ├── colors.xml │ ├── dimens.xml │ ├── strings.xml │ └── styles.xml └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.toptal.com/developers/gitignore/api/androidstudio 2 | # Edit at https://www.toptal.com/developers/gitignore?templates=androidstudio 3 | 4 | ### AndroidStudio ### 5 | # Covers files to be ignored for android development using Android Studio. 6 | 7 | # Built application files 8 | *.apk 9 | *.ap_ 10 | *.aab 11 | 12 | # Files for the ART/Dalvik VM 13 | *.dex 14 | 15 | # Java class files 16 | *.class 17 | 18 | # Generated files 19 | bin/ 20 | gen/ 21 | out/ 22 | 23 | # Gradle files 24 | .gradle 25 | .gradle/ 26 | build/ 27 | 28 | # Signing files 29 | .signing/ 30 | 31 | # Local configuration file (sdk path, etc) 32 | local.properties 33 | 34 | # Proguard folder generated by Eclipse 35 | proguard/ 36 | 37 | # Log Files 38 | *.log 39 | 40 | # Android Studio 41 | /*/build/ 42 | /*/local.properties 43 | /*/out 44 | /*/*/build 45 | /*/*/production 46 | captures/ 47 | .navigation/ 48 | *.ipr 49 | *~ 50 | *.swp 51 | 52 | # Keystore files 53 | *.jks 54 | *.keystore 55 | 56 | # Google Services (e.g. APIs or Firebase) 57 | # google-services.json 58 | 59 | # Android Patch 60 | gen-external-apklibs 61 | 62 | # External native build folder generated in Android Studio 2.2 and later 63 | .externalNativeBuild 64 | 65 | # NDK 66 | obj/ 67 | 68 | # IntelliJ IDEA 69 | *.iml 70 | *.iws 71 | /out/ 72 | 73 | # User-specific configurations 74 | .idea/caches/ 75 | .idea/libraries/ 76 | .idea/shelf/ 77 | .idea/workspace.xml 78 | .idea/tasks.xml 79 | .idea/.name 80 | .idea/compiler.xml 81 | .idea/copyright/profiles_settings.xml 82 | .idea/encodings.xml 83 | .idea/misc.xml 84 | .idea/modules.xml 85 | .idea/scopes/scope_settings.xml 86 | .idea/dictionaries 87 | .idea/vcs.xml 88 | .idea/jsLibraryMappings.xml 89 | .idea/datasources.xml 90 | .idea/dataSources.ids 91 | .idea/sqlDataSources.xml 92 | .idea/dynamic.xml 93 | .idea/uiDesigner.xml 94 | .idea/assetWizardSettings.xml 95 | .idea/gradle.xml 96 | .idea/jarRepositories.xml 97 | .idea/navEditor.xml 98 | 99 | # OS-specific files 100 | .DS_Store 101 | .DS_Store? 102 | ._* 103 | .Spotlight-V100 104 | .Trashes 105 | ehthumbs.db 106 | Thumbs.db 107 | 108 | # Legacy Eclipse project files 109 | .classpath 110 | .project 111 | .cproject 112 | .settings/ 113 | 114 | # Mobile Tools for Java (J2ME) 115 | .mtj.tmp/ 116 | 117 | # Package Files # 118 | *.war 119 | *.ear 120 | 121 | # virtual machine crash logs (Reference: http://www.java.com/en/download/help/error_hotspot.xml) 122 | hs_err_pid* 123 | 124 | ## Plugin-specific files: 125 | 126 | # mpeltonen/sbt-idea plugin 127 | .idea_modules/ 128 | 129 | # JIRA plugin 130 | atlassian-ide-plugin.xml 131 | 132 | # Mongo Explorer plugin 133 | .idea/mongoSettings.xml 134 | 135 | # Crashlytics plugin (for Android Studio and IntelliJ) 136 | com_crashlytics_export_strings.xml 137 | crashlytics.properties 138 | crashlytics-build.properties 139 | fabric.properties 140 | 141 | ### AndroidStudio Patch ### 142 | 143 | !/gradle/wrapper/gradle-wrapper.jar 144 | 145 | # End of https://www.toptal.com/developers/gitignore/api/androidstudio 146 | -------------------------------------------------------------------------------- /.idea/codeStyles/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 135 | -------------------------------------------------------------------------------- /.idea/codeStyles/codeStyleConfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.application' 3 | id 'org.jetbrains.kotlin.android' 4 | id 'kotlin-parcelize' 5 | id 'kotlin-kapt' 6 | } 7 | 8 | 9 | android { 10 | compileSdkVersion 33 11 | 12 | defaultConfig { 13 | applicationId "ru.cloudpayments.demo" 14 | minSdkVersion 23 15 | targetSdkVersion 33 16 | versionCode 1 17 | versionName "1.0" 18 | multiDexEnabled true 19 | manifestPlaceholders = [ 20 | YANDEX_CLIENT_ID: "e7a80db136b0488fb420c8232531fa81" 21 | ] 22 | 23 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 24 | } 25 | 26 | viewBinding { 27 | enabled = true 28 | } 29 | 30 | buildTypes { 31 | debug { 32 | debuggable true 33 | minifyEnabled true 34 | shrinkResources false 35 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 36 | } 37 | release { 38 | debuggable false 39 | minifyEnabled true 40 | shrinkResources true 41 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 42 | } 43 | 44 | applicationVariants.all { variant -> 45 | variant.outputs.all { output -> 46 | def sh = "" 47 | if (!variant.buildType.isDebuggable()) { 48 | sh = getGitShortName() 49 | } 50 | def branch = getGitCurrentBranch() 51 | outputFileName = "${variant.name}-${variant.versionName}(${defaultConfig.versionCode})${sh}_[${branch}].apk" 52 | } 53 | } 54 | } 55 | 56 | 57 | compileOptions { 58 | sourceCompatibility JavaVersion.VERSION_17 59 | targetCompatibility JavaVersion.VERSION_17 60 | } 61 | 62 | kotlinOptions { 63 | jvmTarget = JavaVersion.VERSION_17.toString() 64 | } 65 | namespace 'ru.cloudpayments.demo' 66 | } 67 | 68 | def getGitShortName() { 69 | def stdout = new ByteArrayOutputStream() 70 | 71 | try { 72 | exec { 73 | commandLine 'git', 'rev-parse', '--short', 'HEAD' 74 | standardOutput = stdout 75 | } 76 | } catch (Exception ignored) { 77 | 78 | } 79 | 80 | return stdout.toString().trim() 81 | } 82 | 83 | def getGitCurrentBranch() { 84 | def stdout = new ByteArrayOutputStream() 85 | 86 | try { 87 | exec { 88 | commandLine 'git', 'rev-parse', '--abbrev-ref', 'HEAD' 89 | standardOutput = stdout 90 | } 91 | } catch (Exception ignored) { 92 | 93 | } 94 | 95 | return stdout.toString().trim() 96 | } 97 | 98 | dependencies { 99 | implementation fileTree(dir: "libs", include: ["*.jar"]) 100 | implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" 101 | implementation 'androidx.core:core-ktx:1.9.0' 102 | implementation 'androidx.appcompat:appcompat:1.6.1' 103 | 104 | implementation 'com.google.android.material:material:1.8.0' 105 | implementation 'androidx.cardview:cardview:1.0.0' 106 | implementation 'androidx.constraintlayout:constraintlayout:2.1.4' 107 | implementation 'androidx.fragment:fragment-ktx:1.5.7' 108 | implementation 'androidx.arch.core:core-runtime:2.2.0' 109 | 110 | implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0' 111 | 112 | // material dialogs 113 | implementation 'com.afollestad.material-dialogs:core:0.9.6.0' 114 | 115 | implementation "com.squareup.retrofit2:retrofit:$retrofitVersion" 116 | implementation "com.squareup.retrofit2:adapter-rxjava2:$retrofitVersion" 117 | implementation "com.squareup.retrofit2:converter-gson:$retrofitVersion" 118 | implementation "com.squareup.okhttp3:okhttp:$okHttp3Version" 119 | implementation "com.squareup.okhttp3:logging-interceptor:$okHttp3Version" 120 | 121 | implementation 'ru.tinkoff.decoro:decoro:1.5.0' 122 | 123 | // rxkotlin 124 | implementation 'io.reactivex.rxjava2:rxkotlin:2.1.0' 125 | implementation 'io.reactivex.rxjava2:rxandroid:2.1.1' 126 | implementation "com.jakewharton.rxbinding2:rxbinding-kotlin:$rxBindingVersion" 127 | 128 | implementation 'com.google.android.gms:play-services-wallet:19.1.0' 129 | 130 | //Multidex support 131 | implementation 'androidx.multidex:multidex:2.0.1' 132 | 133 | // dagger2 134 | implementation "com.google.dagger:dagger:$dagger2Version" 135 | implementation "com.google.dagger:dagger-android:$dagger2Version" 136 | implementation "com.google.dagger:dagger-android-support:$dagger2Version" 137 | kapt "com.google.dagger:dagger-compiler:$dagger2Version" 138 | kapt "com.google.dagger:dagger-android-processor:$dagger2Version" 139 | 140 | // image loading and caching 141 | implementation 'com.github.bumptech.glide:glide:4.11.0' 142 | 143 | //card io scanner 144 | implementation 'io.card:android-sdk:5.5.0' 145 | 146 | implementation project(path: ':sdk') 147 | //implementation 'com.github.cloudpayments:CloudPayments-SDK-Android:1.1.3' 148 | } 149 | 150 | ext { 151 | retrofitVersion = '2.5.0' 152 | okHttp3Version = '3.8.0' 153 | } -------------------------------------------------------------------------------- /app/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 22 | 23 | # com.squareup.retrofit2 24 | -dontwarn javax.annotation.** 25 | -dontwarn retrofit2.** 26 | -keep, includedescriptorclasses class retrofit2.** { *; } 27 | -keepattributes Signature 28 | -keepattributes Exceptions 29 | -keepclasseswithmembers class * { 30 | @retrofit2.http.* ; 31 | } 32 | 33 | # com.squareup.okhttp3 34 | -keepattributes Signature 35 | -keepattributes *Annotation* 36 | -keep, includedescriptorclasses class okhttp3.** { *; } 37 | -keep interface okhttp3.** { *; } 38 | -dontwarn okhttp3.** 39 | -keep class okhttp3.internal.platform.** { *; } 40 | -dontwarn okhttp3.internal.platform.** 41 | -dontnote okhttp3.internal.platform.** 42 | 43 | # com.google.gson 44 | -keep class com.google.gson.internal.** { *; } 45 | -dontwarn com.google.gson.internal.** 46 | -dontnote com.google.gson.internal.** 47 | 48 | # ru.cloudpayments.sdk 49 | -keep class ru.cloudpayments.demo.** { *; } 50 | -dontwarn ru.cloudpayments.demo.** 51 | -dontnote ru.cloudpayments.demo.** 52 | 53 | 54 | # Retrofit does reflection on generic parameters. InnerClasses is required to use Signature and 55 | # EnclosingMethod is required to use InnerClasses. 56 | -keepattributes Signature, InnerClasses, EnclosingMethod 57 | 58 | # Retrofit does reflection on method and parameter annotations. 59 | -keepattributes RuntimeVisibleAnnotations, RuntimeVisibleParameterAnnotations 60 | 61 | # Keep annotation default values (e.g., retrofit2.http.Field.encoded). 62 | -keepattributes AnnotationDefault 63 | 64 | # Retain service method parameters when optimizing. 65 | -keepclassmembers,allowshrinking,allowobfuscation interface * { 66 | @retrofit2.http.* ; 67 | } 68 | 69 | # Ignore annotation used for build tooling. 70 | -dontwarn org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement 71 | 72 | # Ignore JSR 305 annotations for embedding nullability information. 73 | -dontwarn javax.annotation.** 74 | 75 | # Guarded by a NoClassDefFoundError try/catch and only used when on the classpath. 76 | -dontwarn kotlin.Unit 77 | 78 | # Top-level functions that can only be used by Kotlin. 79 | -dontwarn retrofit2.KotlinExtensions 80 | -dontwarn retrofit2.KotlinExtensions$* 81 | 82 | # With R8 full mode, it sees no subtypes of Retrofit interfaces since they are created with a Proxy 83 | # and replaces all potential values with null. Explicitly keeping the interfaces prevents this. 84 | -if interface * { @retrofit2.http.* ; } 85 | -keep,allowobfuscation interface <1> 86 | 87 | # Keep inherited services. 88 | -if interface * { @retrofit2.http.* ; } 89 | -keep,allowobfuscation interface * extends <1> 90 | 91 | # With R8 full mode generic signatures are stripped for classes that are not 92 | # kept. Suspend functions are wrapped in continuations where the type argument 93 | # is used. 94 | -keep,allowobfuscation,allowshrinking class kotlin.coroutines.Continuation 95 | 96 | # R8 full mode strips generic signatures from return types if not kept. 97 | -if interface * { @retrofit2.http.* public *** *(...); } 98 | -keep,allowoptimization,allowshrinking,allowobfuscation class <3> 99 | 100 | 101 | # JSR 305 annotations are for embedding nullability information. 102 | -dontwarn javax.annotation.** 103 | 104 | # A resource is loaded with a relative path so the package of this class must be preserved. 105 | -adaptresourcefilenames okhttp3/internal/publicsuffix/PublicSuffixDatabase.gz 106 | 107 | # Animal Sniffer compileOnly dependency to ensure APIs are compatible with older versions of Java. 108 | -dontwarn org.codehaus.mojo.animal_sniffer.* 109 | 110 | # OkHttp platform used only on JVM and when Conscrypt and other security providers are available. 111 | -dontwarn okhttp3.internal.platform.** 112 | -dontwarn org.conscrypt.** 113 | -dontwarn org.bouncycastle.** 114 | -dontwarn org.openjsse.** -------------------------------------------------------------------------------- /app/release/output-metadata.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 2, 3 | "artifactType": { 4 | "type": "APK", 5 | "kind": "Directory" 6 | }, 7 | "applicationId": "ru.cloudpayments.demo", 8 | "variantName": "release", 9 | "elements": [ 10 | { 11 | "type": "SINGLE", 12 | "filters": [], 13 | "versionCode": 1, 14 | "versionName": "1.0", 15 | "outputFile": "release-1.0(1)f762b6f_[master].apk" 16 | } 17 | ] 18 | } -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 24 | 25 | -------------------------------------------------------------------------------- /app/src/main/ic_launcher-playstore.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudpayments/CloudPayments-SDK-Android/2f022fe6f242486cc000a1a16ffa03df2def5049/app/src/main/ic_launcher-playstore.png -------------------------------------------------------------------------------- /app/src/main/java/ru/cloudpayments/demo/Constants.kt: -------------------------------------------------------------------------------- 1 | package ru.cloudpayments.demo 2 | 3 | class Constants { 4 | companion object { 5 | const val merchantPublicId = "test_api_00000000000000000000002" 6 | } 7 | } -------------------------------------------------------------------------------- /app/src/main/java/ru/cloudpayments/demo/api/PayApi.kt: -------------------------------------------------------------------------------- 1 | package ru.cloudpayments.demo.api 2 | 3 | import io.reactivex.Observable 4 | import ru.cloudpayments.sdk.api.models.* 5 | import ru.cloudpayments.sdk.configuration.CloudpaymentsSDK 6 | import ru.cloudpayments.demo.Constants 7 | 8 | class PayApi { 9 | companion object { 10 | private val api = CloudpaymentsSDK.createApi(Constants.merchantPublicId) 11 | 12 | fun charge(cardCryptogramPacket: String, cardHolderName: String?, amount: Int): Observable { 13 | // Параметры см. в PaymentRequestBody 14 | val body = PaymentRequestBody(amount = amount.toString(), 15 | currency = "RUB", 16 | ipAddress = "", 17 | name = cardHolderName.orEmpty(), 18 | cryptogram = cardCryptogramPacket) 19 | return api.charge(body) 20 | .toObservable() 21 | .flatMap(CloudpaymentsTransactionResponse::handleError) 22 | .map { it.transaction } 23 | } 24 | 25 | fun auth(cardCryptogramPacket: String, cardHolderName: String?, amount: Int): Observable { 26 | // Параметры см. в PaymentRequestBody 27 | val body = PaymentRequestBody(amount = amount.toString(), 28 | currency = "RUB", 29 | ipAddress = "", 30 | name = cardHolderName.orEmpty(), 31 | cryptogram = cardCryptogramPacket) 32 | return api.auth(body) 33 | .toObservable() 34 | .flatMap(CloudpaymentsTransactionResponse::handleError) 35 | .map { it.transaction } 36 | } 37 | 38 | fun postThreeDs(transactionId: String, threeDsCallbackId: String, paRes: String): Observable { 39 | return api.postThreeDs(transactionId, threeDsCallbackId, paRes) 40 | .toObservable() 41 | } 42 | 43 | fun getBinInfo(firstSixDigits: String): Observable { 44 | return api.getBinInfo(firstSixDigits) 45 | .toObservable() 46 | } 47 | } 48 | } -------------------------------------------------------------------------------- /app/src/main/java/ru/cloudpayments/demo/base/BaseActivity.kt: -------------------------------------------------------------------------------- 1 | package ru.cloudpayments.demo.base 2 | 3 | import android.os.Bundle 4 | import android.util.Log 5 | import android.view.MenuItem 6 | import android.widget.Toast 7 | import androidx.annotation.StringRes 8 | import androidx.appcompat.app.AppCompatActivity 9 | import com.afollestad.materialdialogs.MaterialDialog 10 | import io.reactivex.disposables.CompositeDisposable 11 | import ru.cloudpayments.sdk.api.models.CloudpaymentsTransactionError 12 | import ru.cloudpayments.demo.R 13 | import ru.cloudpayments.demo.databinding.ToolbarBinding 14 | import java.net.UnknownHostException 15 | import java.util.* 16 | 17 | abstract class BaseActivity : AppCompatActivity() { 18 | protected val TAG = "TAG_" + javaClass.simpleName.toUpperCase(Locale.getDefault()) 19 | protected var compositeDisposable = CompositeDisposable() 20 | private var loadingDialog: MaterialDialog? = null 21 | protected abstract val layoutId: Int 22 | 23 | 24 | override fun onCreate(savedInstanceState: Bundle?) { 25 | super.onCreate(savedInstanceState) 26 | val toolbar = ToolbarBinding.inflate(layoutInflater).root 27 | setSupportActionBar(toolbar) 28 | supportActionBar?.setDisplayHomeAsUpEnabled(true) 29 | initLoadingDialog() 30 | } 31 | 32 | private fun initLoadingDialog() { 33 | loadingDialog = MaterialDialog.Builder(this) 34 | .progress(true, 0) 35 | .title(R.string.dialog_loading_title) 36 | .content(R.string.dialog_loading_content) 37 | .cancelable(false) 38 | .build() 39 | } 40 | 41 | 42 | /*@Override 43 | public void onDestroy() { 44 | super.onDestroy(); 45 | compositeSubscription.unsubscribe(); 46 | }*/ 47 | 48 | fun showLoading() { 49 | if (loadingDialog?.isShowing == true) { 50 | return 51 | } 52 | loadingDialog?.show() 53 | } 54 | 55 | fun hideLoading() { 56 | if (loadingDialog?.isShowing != true) { 57 | return 58 | } 59 | loadingDialog?.dismiss() 60 | } 61 | 62 | override fun onOptionsItemSelected(item: MenuItem): Boolean { 63 | when (item.itemId) { 64 | android.R.id.home -> { 65 | onBackPressed() 66 | return true 67 | } 68 | } 69 | return super.onOptionsItemSelected(item) 70 | } 71 | 72 | fun showToast(@StringRes resId: Int) { 73 | showToast(getString(resId)) 74 | } 75 | 76 | fun showToast(message: String?) { 77 | Toast.makeText(this, message, Toast.LENGTH_SHORT).show() 78 | } 79 | 80 | fun log(message: String?) { 81 | Log.d(TAG, message.orEmpty()) 82 | } 83 | 84 | fun handleError(throwable: Throwable, vararg ignoreClasses: Class<*>?) { 85 | if (ignoreClasses.isNotEmpty()) { 86 | val classList = listOf(*ignoreClasses) 87 | if (classList.contains(throwable.javaClass)) { 88 | return 89 | } 90 | } 91 | when (throwable) { 92 | is CloudpaymentsTransactionError -> { 93 | val message: String = throwable.message 94 | showToast(message) 95 | } 96 | is UnknownHostException -> showToast(R.string.common_no_internet_connection) 97 | else -> showToast(throwable.message) 98 | } 99 | } 100 | } -------------------------------------------------------------------------------- /app/src/main/java/ru/cloudpayments/demo/base/BaseAdapter.kt: -------------------------------------------------------------------------------- 1 | package ru.cloudpayments.demo.base 2 | 3 | import androidx.recyclerview.widget.RecyclerView 4 | 5 | abstract class BaseAdapter : RecyclerView.Adapter() -------------------------------------------------------------------------------- /app/src/main/java/ru/cloudpayments/demo/base/BaseListActivity.kt: -------------------------------------------------------------------------------- 1 | package ru.cloudpayments.demo.base 2 | 3 | import androidx.recyclerview.widget.RecyclerView 4 | 5 | abstract class BaseListActivity?> : BaseActivity() { 6 | protected var adapter: T? = null 7 | } -------------------------------------------------------------------------------- /app/src/main/java/ru/cloudpayments/demo/googlepay/ConstantsGPay.kt: -------------------------------------------------------------------------------- 1 | package ru.cloudpayments.demo.googlepay 2 | 3 | import android.util.Pair 4 | import com.google.android.gms.wallet.WalletConstants 5 | import ru.cloudpayments.demo.support.Constants 6 | import java.util.* 7 | 8 | object ConstantsGPay { 9 | // This file contains several constants you must edit before proceeding. Once you're done, 10 | // remove this static block and run the sample. 11 | // Before you start, please take a look at PaymentsUtil.java to see where the constants are used 12 | // and to potentially remove ones not relevant to your integration. 13 | // Required changes: 14 | // 1. Update SUPPORTED_NETWORKS and SUPPORTED_METHODS if required (consult your processor if 15 | // unsure). 16 | // 2. Update CURRENCY_CODE to the currency you use. 17 | // 3. Update SHIPPING_SUPPORTED_COUNTRIES to list the countries where you currently ship. If 18 | // this is not applicable to your app, remove the relevant bits from PaymentsUtil.java. 19 | // 4. If you're integrating with your processor / gateway directly, update 20 | // GATEWAY_TOKENIZATION_NAME and GATEWAY_TOKENIZATION_PARAMETERS per the instructions they 21 | // provided. You don't need to update DIRECT_TOKENIZATION_PUBLIC_KEY. 22 | // 5. If you're using direct integration, please consult the documentation to learn about 23 | // next steps. 24 | // Changing this to ENVIRONMENT_PRODUCTION will make the API return real card information. 25 | // Please refer to the documentation to read about the required steps needed to enable 26 | // ENVIRONMENT_PRODUCTION. 27 | const val PAYMENTS_ENVIRONMENT = WalletConstants.ENVIRONMENT_TEST 28 | 29 | // The allowed networks to be requested from the API. If the user has cards from networks not 30 | // specified here in their account, these will not be offered for them to choose in the popup. 31 | val SUPPORTED_NETWORKS = Arrays.asList( 32 | WalletConstants.CARD_NETWORK_VISA, 33 | WalletConstants.CARD_NETWORK_MASTERCARD 34 | ) 35 | val SUPPORTED_METHODS = 36 | Arrays.asList( // PAYMENT_METHOD_CARD returns to any card the user has stored in their Google Account. 37 | WalletConstants.PAYMENT_METHOD_CARD, // PAYMENT_METHOD_TOKENIZED_CARD refers to cards added to Android Pay, assuming Android 38 | // Pay is installed. 39 | // Please keep in mind cards may exist in Android Pay without being added to the Google 40 | // Account. 41 | WalletConstants.PAYMENT_METHOD_TOKENIZED_CARD 42 | ) 43 | 44 | // Required by the API, but not visible to the user. 45 | const val CURRENCY_CODE = "RUB" 46 | 47 | // The name of your payment processor / gateway. Please refer to their documentation for 48 | // more information. 49 | const val GATEWAY_TOKENIZATION_NAME = "cloudpayments" 50 | 51 | // Custom parameters required by the processor / gateway. 52 | // In many cases, your processor / gateway will only require a gatewayMerchantId. 53 | // Please refer to your processor's documentation for more information. The number of parameters 54 | // required and their names vary depending on the processor. 55 | val GATEWAY_TOKENIZATION_PARAMETERS = Arrays.asList( 56 | Pair.create("gatewayMerchantId", Constants.MERCHANT_PUBLIC_ID) 57 | ) 58 | } -------------------------------------------------------------------------------- /app/src/main/java/ru/cloudpayments/demo/managers/CartManager.kt: -------------------------------------------------------------------------------- 1 | package ru.cloudpayments.demo.managers 2 | 3 | import ru.cloudpayments.demo.models.Product 4 | import kotlin.collections.ArrayList 5 | 6 | class CartManager private constructor() { 7 | private var products: ArrayList = arrayListOf() 8 | 9 | fun clear() { 10 | products.clear() 11 | } 12 | 13 | fun getProducts() = products 14 | 15 | fun setProducts(products: ArrayList?) { 16 | this.products = ArrayList(products.orEmpty()) 17 | } 18 | 19 | fun addProduct(product: Product) { 20 | products.add(product) 21 | } 22 | 23 | companion object { 24 | private var instance: CartManager? = null 25 | @Synchronized fun getInstance(): CartManager? { 26 | if (instance == null) { 27 | synchronized(CartManager::class.java) { instance = CartManager() } 28 | } 29 | return instance 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /app/src/main/java/ru/cloudpayments/demo/models/Product.kt: -------------------------------------------------------------------------------- 1 | package ru.cloudpayments.demo.models 2 | 3 | import com.google.gson.annotations.SerializedName 4 | import ru.cloudpayments.demo.R 5 | 6 | data class Product( 7 | @SerializedName("id") val id: String = "1", 8 | @SerializedName("name") val name: String = "Букет \"Нежность\"", 9 | @SerializedName("price") val price: String = "1", 10 | @SerializedName("image") val image: Int = R.drawable.product) 11 | -------------------------------------------------------------------------------- /app/src/main/java/ru/cloudpayments/demo/screens/main/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package ru.cloudpayments.demo.screens.main 2 | 3 | import android.os.Bundle 4 | import android.widget.Toast 5 | import ru.cloudpayments.demo.R 6 | import ru.cloudpayments.demo.base.BaseActivity 7 | import ru.cloudpayments.demo.databinding.ActivityMainBinding 8 | import ru.cloudpayments.demo.support.CardIOScanner 9 | import ru.cloudpayments.sdk.api.models.PaymentDataPayer 10 | import ru.cloudpayments.sdk.configuration.CloudpaymentsSDK 11 | import ru.cloudpayments.sdk.configuration.PaymentConfiguration 12 | import ru.cloudpayments.sdk.configuration.PaymentData 13 | 14 | class MainActivity : BaseActivity() { 15 | 16 | private val cpSdkLauncher = CloudpaymentsSDK.getInstance().launcher(this, result = { 17 | if (it.status != null) { 18 | if (it.status == CloudpaymentsSDK.TransactionStatus.Succeeded) { 19 | Toast.makeText(this, "Успешно! Транзакция №${it.transactionId}", Toast.LENGTH_SHORT).show() 20 | } else { 21 | if (it.reasonCode != 0) { 22 | Toast.makeText(this, "Ошибка! Транзакция №${it.transactionId}. Код ошибки ${it.reasonCode}", Toast.LENGTH_SHORT).show() 23 | } else { 24 | Toast.makeText(this, "Ошибка! Транзакция №${it.transactionId}.", Toast.LENGTH_SHORT).show() 25 | } 26 | } 27 | } 28 | }) 29 | 30 | override val layoutId = R.layout.activity_main 31 | 32 | private lateinit var binding: ActivityMainBinding 33 | 34 | override fun onCreate(savedInstanceState: Bundle?) { 35 | super.onCreate(savedInstanceState) 36 | 37 | binding = ActivityMainBinding.inflate(layoutInflater) 38 | val view = binding.root 39 | setContentView(view) 40 | 41 | binding.buttonRunTop.setOnClickListener { 42 | runCpSdk() 43 | } 44 | 45 | binding.buttonRun.setOnClickListener { 46 | runCpSdk() 47 | } 48 | } 49 | 50 | private fun runCpSdk() { 51 | 52 | val apiUrl = binding.editApiUrl.text.toString() 53 | val publicId = binding.editPublicId.text.toString() 54 | val amount = binding.editAmount.text.toString() 55 | val currency = binding.editCurrency.text.toString() 56 | val invoiceId = binding.editInvoiceId.text.toString() 57 | val description = binding.editDescription.text.toString() 58 | val accountId = binding.editAccountId.text.toString() 59 | val email = binding.editEmail.text.toString() 60 | 61 | val payerFirstName = binding.editPayerFirstName.text.toString() 62 | val payerLastName = binding.editPayerLastName.text.toString() 63 | val payerMiddleName = binding.editPayerMiddleName.text.toString() 64 | val payerBirthDay = binding.editPayerBirth.text.toString() 65 | val payerAddress = binding.editPayerAddress.text.toString() 66 | val payerStreet = binding.editPayerStreet.text.toString() 67 | val payerCity = binding.editPayerCity.text.toString() 68 | val payerCountry = binding.editPayerCountry.text.toString() 69 | val payerPhone = binding.editPayerPhone.text.toString() 70 | val payerPostcode = binding.editPayerPostcode.text.toString() 71 | 72 | val jsonData = binding.editJsonData.text.toString() 73 | val isDualMessagePayment = binding.checkboxDualMessagePayment.isChecked 74 | 75 | var payer = PaymentDataPayer() 76 | payer.firstName = payerFirstName 77 | payer.lastName = payerLastName 78 | payer.middleName = payerMiddleName 79 | payer.birthDay = payerBirthDay 80 | payer.address = payerAddress 81 | payer.street = payerStreet 82 | payer.city = payerCity 83 | payer.country = payerCountry 84 | payer.phone = payerPhone 85 | payer.postcode = payerPostcode 86 | 87 | val paymentData = PaymentData( 88 | amount = amount, 89 | currency = currency, 90 | invoiceId = invoiceId, 91 | description = description, 92 | accountId = accountId, 93 | email = email, 94 | payer = payer, 95 | jsonData = jsonData 96 | ) 97 | 98 | val configuration = PaymentConfiguration( 99 | publicId = publicId, 100 | paymentData = paymentData, 101 | scanner = CardIOScanner(), 102 | requireEmail = false, 103 | useDualMessagePayment = isDualMessagePayment, 104 | disableGPay = false, 105 | disableYandexPay = false, 106 | yandexPayMerchantID = "", 107 | apiUrl = apiUrl 108 | ) 109 | cpSdkLauncher.launch(configuration) 110 | } 111 | } -------------------------------------------------------------------------------- /app/src/main/java/ru/cloudpayments/demo/support/CardIOScanner.kt: -------------------------------------------------------------------------------- 1 | package ru.cloudpayments.demo.support 2 | 3 | import android.content.Context 4 | import android.content.Intent 5 | import io.card.payment.CardIOActivity 6 | import io.card.payment.CreditCard 7 | import kotlinx.android.parcel.Parcelize 8 | import ru.cloudpayments.sdk.scanner.CardData 9 | import ru.cloudpayments.sdk.scanner.CardScanner 10 | 11 | 12 | @Parcelize 13 | class CardIOScanner: CardScanner() { 14 | override fun getScannerIntent(context: Context) = 15 | Intent(context, CardIOActivity::class.java).apply { 16 | putExtra(CardIOActivity.EXTRA_REQUIRE_EXPIRY, true) 17 | } 18 | 19 | override fun getCardDataFromIntent(data: Intent) = 20 | if (data.hasExtra(CardIOActivity.EXTRA_SCAN_RESULT)) { 21 | val scanResult = data.getParcelableExtra(CardIOActivity.EXTRA_SCAN_RESULT) as? CreditCard 22 | val month = (scanResult?.expiryMonth ?: 0).toString().padStart(2, '0') 23 | val yearString = scanResult?.expiryYear?.toString() ?: "00" 24 | val year = if (yearString.length > 2) { 25 | yearString.substring(yearString.lastIndex - 1) 26 | } else { 27 | yearString.padStart(2, '0') 28 | } 29 | val cardData = CardData(scanResult?.cardNumber, month, year, scanResult?.cardholderName) 30 | cardData 31 | } else { 32 | null 33 | } 34 | } -------------------------------------------------------------------------------- /app/src/main/java/ru/cloudpayments/demo/support/Constants.kt: -------------------------------------------------------------------------------- 1 | package ru.cloudpayments.demo.support 2 | 3 | object Constants { 4 | const val CONSUMER_KEY = "ck_ddb320b48b89a170248545eb3bb8e822365aa917" 5 | const val CONSUMER_SECRET = "cs_35ad6d0cf8e6b149e66968efdad87112ca2bc2d3" 6 | const val MERCHANT_PUBLIC_ID = "test_api_00000000000000000000002" // Test host 7 | } -------------------------------------------------------------------------------- /app/src/main/java/ru/cloudpayments/demo/support/SideSpaceItemDecoration.kt: -------------------------------------------------------------------------------- 1 | package ru.cloudpayments.demo.support 2 | 3 | import android.content.Context 4 | import android.graphics.Rect 5 | import android.view.View 6 | import androidx.recyclerview.widget.RecyclerView 7 | import androidx.recyclerview.widget.RecyclerView.ItemDecoration 8 | 9 | class SideSpaceItemDecoration(context: Context, spacing: Int, private val spanCount: Int, private val includeEdge: Boolean) : ItemDecoration() { 10 | private val spacingInDP: Float = spacing * context.resources.displayMetrics.density 11 | 12 | override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State) { 13 | val position = parent.getChildAdapterPosition(view) 14 | val column = position % spanCount 15 | if (includeEdge) { 16 | outRect.left = (spacingInDP - column * spacingInDP / spanCount).toInt() 17 | outRect.right = ((column + 1) * spacingInDP / spanCount).toInt() 18 | if (position < spanCount) { 19 | outRect.top = spacingInDP.toInt() 20 | } 21 | outRect.bottom = spacingInDP.toInt() 22 | } else { 23 | outRect.left = (column * spacingInDP / spanCount).toInt() 24 | outRect.right = (spacingInDP - (column + 1) * spacingInDP / spanCount).toInt() 25 | if (position >= spanCount) { 26 | outRect.top = spacingInDP.toInt() 27 | } 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 15 | 18 | 21 | 22 | 23 | 24 | 30 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudpayments/CloudPayments-SDK-Android/2f022fe6f242486cc000a1a16ffa03df2def5049/app/src/main/res/drawable-xxhdpi/logo.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/product.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudpayments/CloudPayments-SDK-Android/2f022fe6f242486cc000a1a16ffa03df2def5049/app/src/main/res/drawable-xxhdpi/product.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/splash_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudpayments/CloudPayments-SDK-Android/2f022fe6f242486cc000a1a16ffa03df2def5049/app/src/main/res/drawable-xxhdpi/splash_logo.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/googlepay_button_content.xml: -------------------------------------------------------------------------------- 1 | 6 | 12 | 18 | 24 | 30 | 36 | 42 | 48 | 49 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/googlepay_button_overlay.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_credit_card_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_date_range_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_email_white_24dp.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_lock_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_person_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_phone_white_24dp.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_shopping_cart_white_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/splashscreen_bg.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 7 | 8 | 9 | 10 | 11 | 12 | 15 | 16 | -------------------------------------------------------------------------------- /app/src/main/res/layout/googlepay_button.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 15 | 22 | 23 | 29 | 30 | -------------------------------------------------------------------------------- /app/src/main/res/layout/toolbar.xml: -------------------------------------------------------------------------------- 1 | 2 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/menu/main.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudpayments/CloudPayments-SDK-Android/2f022fe6f242486cc000a1a16ffa03df2def5049/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudpayments/CloudPayments-SDK-Android/2f022fe6f242486cc000a1a16ffa03df2def5049/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudpayments/CloudPayments-SDK-Android/2f022fe6f242486cc000a1a16ffa03df2def5049/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudpayments/CloudPayments-SDK-Android/2f022fe6f242486cc000a1a16ffa03df2def5049/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudpayments/CloudPayments-SDK-Android/2f022fe6f242486cc000a1a16ffa03df2def5049/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudpayments/CloudPayments-SDK-Android/2f022fe6f242486cc000a1a16ffa03df2def5049/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudpayments/CloudPayments-SDK-Android/2f022fe6f242486cc000a1a16ffa03df2def5049/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudpayments/CloudPayments-SDK-Android/2f022fe6f242486cc000a1a16ffa03df2def5049/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudpayments/CloudPayments-SDK-Android/2f022fe6f242486cc000a1a16ffa03df2def5049/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudpayments/CloudPayments-SDK-Android/2f022fe6f242486cc000a1a16ffa03df2def5049/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #6494DA 4 | #393861 5 | #6494DA 6 | -------------------------------------------------------------------------------- /app/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 19 | 20 | 16dp 21 | 22 | 5dp 23 | 10dp 24 | 30dp 25 | 26 | 300dp 27 | 48sp 28 | 200dp 29 | 30 | 31 | -------------------------------------------------------------------------------- /app/src/main/res/values/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #FFFFFF 4 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | CloudPayments Demo 3 | 4 | My Online Store 5 | Unfortunately, Pay with Google is not available on this phone. 6 | Checking if Pay with Google is available... 7 | Successfully received payment data for %s! 8 | 9 | Оплатить через Google 10 | 11 | 12 | Загрузка данных 13 | Пожалуйста, подождите… 14 | Для этого действия необходима авторизация 15 | Сервер недоступен 16 | 17 | 18 | Отсутствует соединение с интернетом 19 | 20 | 21 | CloudPayments 22 | Посмотрите, как выглядят платежи через CloudPayments для покупателей в мобильном приложении. Выберите товары, которые нужно приобрести. Товары не настоящие, платежи тоже — деньги тратить не придется. 23 | В корзину 24 | +7 495 374-78-60 25 | sales@cloudpayments.ru 26 | Выберите приложение 27 | Ваша корзина пуста добавьте один или несколько товаров чтобы продолжить. 28 | Товар добавлен в корзину. 29 | Руб. 30 | 31 | Одностадийная оплата 32 | Двухстадийная оплата 33 | 34 | 35 | 36 | Корзина 37 | Итого: 38 | Итого: %s Руб. 39 | Перейти к оплате 40 | 41 | 42 | Оплата 43 | Так выглядят платежи через CloudPayments для покупателей в мобильном приложении. Платеж тестовый — деньги тратить не придется. 44 | Всего к оплате: 45 | Всего к оплате: %s Руб. 46 | Номер карты 47 | MM/YY 48 | CVC 49 | Владелец карты 50 | Оплатить 51 | 52 | 53 | ВНИМАНИЕ!\nВ режиме PRODUCTION с вашей банковской карты будет списан 1 рубль! 54 | Вы можете провести оплату введя данные банковской карты или используя Google Pay если этот сервис доступен на Вашем устройстве 55 | Срок действия в формате: MMYY 56 | Имя владельца 57 | CVV код 58 | Оплатить 59 | или 60 | 61 | Номер карты некорректен 62 | Срок действитя некорректен либо истек 63 | Некорректный CVC код 64 | 65 | Невозможно создать криптограмму проверьте данные карты 66 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 10 | 11 | 15 | 16 | 23 | 24 | 30 | 31 | 35 | 36 | 43 | 44 | 50 | 51 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | plugins { 3 | id 'com.android.application' version '8.0.0' apply false 4 | id 'com.android.library' version '8.0.0' apply false 5 | id 'org.jetbrains.kotlin.android' version '1.7.10' apply false 6 | } 7 | 8 | task clean(type: Delete) { 9 | delete rootProject.buildDir 10 | } 11 | 12 | ext { 13 | kotlin_version = "1.7.20" 14 | retrofitVersion = '2.9.0' 15 | okHttp3Version = '4.5.0' 16 | rxBindingVersion = '2.2.0' 17 | dagger2Version = '2.45' 18 | } -------------------------------------------------------------------------------- /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.useAndroidX=true 18 | # Automatically convert third-party libraries to use AndroidX 19 | android.enableJetifier=true 20 | # Kotlin code style for this project: "official" or "obsolete": 21 | kotlin.code.style=official 22 | org.gradle.unsafe.configuration-cache = true 23 | android.defaults.buildfeatures.buildconfig = true 24 | android.nonTransitiveRClass = false 25 | android.nonFinalResIds = false -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudpayments/CloudPayments-SDK-Android/2f022fe6f242486cc000a1a16ffa03df2def5049/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Tue Aug 01 11:57:48 YEKT 2023 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip 5 | zipStoreBase=GRADLE_USER_HOME 6 | zipStorePath=wrapper/dists 7 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /local.properties: -------------------------------------------------------------------------------- 1 | ## This file must *NOT* be checked into Version Control Systems, 2 | # as it contains information specific to your local configuration. 3 | # 4 | # Location of the SDK. This is only used by Gradle. 5 | # For customization when using a Version Control System, please read the 6 | # header note. 7 | #Fri Jan 22 15:32:23 YEKT 2021 8 | sdk.dir=/Users/a.ignatov/Library/Android/sdk 9 | -------------------------------------------------------------------------------- /sdk/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /sdk/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.library' 3 | id 'org.jetbrains.kotlin.android' 4 | id 'kotlin-parcelize' 5 | id 'kotlin-kapt' 6 | } 7 | 8 | android { 9 | compileSdkVersion 33 10 | 11 | defaultConfig { 12 | minSdkVersion 23 13 | targetSdkVersion 33 14 | multiDexEnabled false 15 | 16 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 17 | consumerProguardFiles "consumer-rules.pro" 18 | } 19 | 20 | viewBinding { 21 | enabled = true 22 | } 23 | 24 | buildTypes { 25 | debug { 26 | minifyEnabled true 27 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 28 | consumerProguardFiles 'proguard-rules.pro' 29 | } 30 | release { 31 | minifyEnabled true 32 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 33 | consumerProguardFiles 'proguard-rules.pro' 34 | } 35 | } 36 | 37 | compileOptions { 38 | sourceCompatibility JavaVersion.VERSION_17 39 | targetCompatibility JavaVersion.VERSION_17 40 | } 41 | 42 | kotlinOptions { 43 | jvmTarget = JavaVersion.VERSION_17.toString() 44 | } 45 | namespace 'ru.cloudpayments.sdk' 46 | } 47 | 48 | dependencies { 49 | implementation fileTree(dir: "libs", include: ["*.jar"]) 50 | implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" 51 | implementation 'androidx.core:core-ktx:1.9.0' 52 | implementation 'androidx.appcompat:appcompat:1.6.1' 53 | 54 | implementation 'com.google.android.material:material:1.8.0' 55 | implementation 'androidx.cardview:cardview:1.0.0' 56 | implementation 'androidx.constraintlayout:constraintlayout:2.1.4' 57 | implementation 'androidx.fragment:fragment-ktx:1.5.7' 58 | implementation 'androidx.arch.core:core-runtime:2.2.0' 59 | 60 | implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0' 61 | 62 | implementation "com.squareup.retrofit2:retrofit:$retrofitVersion" 63 | implementation "com.squareup.retrofit2:adapter-rxjava2:$retrofitVersion" 64 | implementation "com.squareup.retrofit2:converter-gson:$retrofitVersion" 65 | implementation "com.squareup.okhttp3:okhttp:$okHttp3Version" 66 | implementation "com.squareup.okhttp3:logging-interceptor:$okHttp3Version" 67 | 68 | implementation 'ru.tinkoff.decoro:decoro:1.5.0' 69 | 70 | implementation 'org.jsoup:jsoup:1.13.1' 71 | 72 | // rxkotlin 73 | implementation 'io.reactivex.rxjava2:rxkotlin:2.1.0' 74 | implementation 'io.reactivex.rxjava2:rxandroid:2.1.1' 75 | implementation "com.jakewharton.rxbinding2:rxbinding-kotlin:$rxBindingVersion" 76 | 77 | implementation 'com.google.android.gms:play-services-wallet:19.1.0' 78 | 79 | //Multidex support 80 | implementation 'androidx.multidex:multidex:2.0.1' 81 | 82 | // dagger2 83 | implementation "com.google.dagger:dagger:$dagger2Version" 84 | implementation "com.google.dagger:dagger-android:$dagger2Version" 85 | implementation "com.google.dagger:dagger-android-support:$dagger2Version" 86 | kapt "com.google.dagger:dagger-compiler:$dagger2Version" 87 | kapt "com.google.dagger:dagger-android-processor:$dagger2Version" 88 | 89 | //rx google-play-services 90 | implementation 'io.ashdavies.rx.rxtasks:rx-tasks:2.2.0' 91 | 92 | // yandex pay 93 | implementation 'com.yandex.pay:core:0.2.2' 94 | } -------------------------------------------------------------------------------- /sdk/consumer-rules.pro: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudpayments/CloudPayments-SDK-Android/2f022fe6f242486cc000a1a16ffa03df2def5049/sdk/consumer-rules.pro -------------------------------------------------------------------------------- /sdk/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 22 | 23 | # com.squareup.retrofit2 24 | -dontwarn javax.annotation.** 25 | -dontwarn retrofit2.** 26 | -keep, includedescriptorclasses class retrofit2.** { *; } 27 | -keepattributes Signature 28 | -keepattributes Exceptions 29 | -keepclasseswithmembers class * { 30 | @retrofit2.http.* ; 31 | } 32 | 33 | # com.squareup.okhttp3 34 | -keepattributes Signature 35 | -keepattributes *Annotation* 36 | -keep, includedescriptorclasses class okhttp3.** { *; } 37 | -keep interface okhttp3.** { *; } 38 | -dontwarn okhttp3.** 39 | -keep class okhttp3.internal.platform.** { *; } 40 | -dontwarn okhttp3.internal.platform.** 41 | -dontnote okhttp3.internal.platform.** 42 | 43 | # com.google.gson 44 | -keep class com.google.gson.internal.** { *; } 45 | -dontwarn com.google.gson.internal.** 46 | -dontnote com.google.gson.internal.** 47 | 48 | # ru.cloudpayments.sdk 49 | -keep class ru.cloudpayments.sdk.** { *; } 50 | -dontwarn ru.cloudpayments.sdk.** 51 | -dontnote ru.cloudpayments.sdk.** 52 | 53 | 54 | # Retrofit does reflection on generic parameters. InnerClasses is required to use Signature and 55 | # EnclosingMethod is required to use InnerClasses. 56 | -keepattributes Signature, InnerClasses, EnclosingMethod 57 | 58 | # Retrofit does reflection on method and parameter annotations. 59 | -keepattributes RuntimeVisibleAnnotations, RuntimeVisibleParameterAnnotations 60 | 61 | # Keep annotation default values (e.g., retrofit2.http.Field.encoded). 62 | -keepattributes AnnotationDefault 63 | 64 | # Retain service method parameters when optimizing. 65 | -keepclassmembers,allowshrinking,allowobfuscation interface * { 66 | @retrofit2.http.* ; 67 | } 68 | 69 | # Ignore annotation used for build tooling. 70 | -dontwarn org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement 71 | 72 | # Ignore JSR 305 annotations for embedding nullability information. 73 | -dontwarn javax.annotation.** 74 | 75 | # Guarded by a NoClassDefFoundError try/catch and only used when on the classpath. 76 | -dontwarn kotlin.Unit 77 | 78 | # Top-level functions that can only be used by Kotlin. 79 | -dontwarn retrofit2.KotlinExtensions 80 | -dontwarn retrofit2.KotlinExtensions$* 81 | 82 | # With R8 full mode, it sees no subtypes of Retrofit interfaces since they are created with a Proxy 83 | # and replaces all potential values with null. Explicitly keeping the interfaces prevents this. 84 | -if interface * { @retrofit2.http.* ; } 85 | -keep,allowobfuscation interface <1> 86 | 87 | # Keep inherited services. 88 | -if interface * { @retrofit2.http.* ; } 89 | -keep,allowobfuscation interface * extends <1> 90 | 91 | # With R8 full mode generic signatures are stripped for classes that are not 92 | # kept. Suspend functions are wrapped in continuations where the type argument 93 | # is used. 94 | -keep,allowobfuscation,allowshrinking class kotlin.coroutines.Continuation 95 | 96 | # R8 full mode strips generic signatures from return types if not kept. 97 | -if interface * { @retrofit2.http.* public *** *(...); } 98 | -keep,allowoptimization,allowshrinking,allowobfuscation class <3> 99 | 100 | 101 | # JSR 305 annotations are for embedding nullability information. 102 | -dontwarn javax.annotation.** 103 | 104 | # A resource is loaded with a relative path so the package of this class must be preserved. 105 | -adaptresourcefilenames okhttp3/internal/publicsuffix/PublicSuffixDatabase.gz 106 | 107 | # Animal Sniffer compileOnly dependency to ensure APIs are compatible with older versions of Java. 108 | -dontwarn org.codehaus.mojo.animal_sniffer.* 109 | 110 | # OkHttp platform used only on JVM and when Conscrypt and other security providers are available. 111 | -dontwarn okhttp3.internal.platform.** 112 | -dontwarn org.conscrypt.** 113 | -dontwarn org.bouncycastle.** 114 | -dontwarn org.openjsse.** -------------------------------------------------------------------------------- /sdk/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | -------------------------------------------------------------------------------- /sdk/src/main/java/ru/cloudpayments/sdk/Constants.kt: -------------------------------------------------------------------------------- 1 | package ru.cloudpayments.sdk 2 | 3 | class Constants { 4 | companion object { 5 | const val baseApiUrl = "https://api.cloudpayments.ru/" 6 | } 7 | } -------------------------------------------------------------------------------- /sdk/src/main/java/ru/cloudpayments/sdk/api/AuthenticationInterceptor.kt: -------------------------------------------------------------------------------- 1 | package ru.cloudpayments.sdk.api 2 | 3 | import okhttp3.HttpUrl 4 | import okhttp3.Interceptor 5 | import okhttp3.Request 6 | import okhttp3.Response 7 | import java.io.IOException 8 | 9 | class AuthenticationInterceptor (private val publicId: String) : Interceptor { 10 | 11 | @Throws(IOException::class) 12 | override fun intercept(chain: Interceptor.Chain): Response { 13 | val original: Request = chain.request() 14 | val originalHttpUrl: HttpUrl = original.url 15 | 16 | val builder = originalHttpUrl.newBuilder() 17 | builder.addQueryParameter("publicId", publicId) 18 | 19 | val url = builder.build() 20 | 21 | val requestBuilder: Request.Builder = original.newBuilder() 22 | .url(url) 23 | 24 | val request: Request = requestBuilder.build() 25 | return chain.proceed(request) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /sdk/src/main/java/ru/cloudpayments/sdk/api/CloudpaymentsApi.kt: -------------------------------------------------------------------------------- 1 | package ru.cloudpayments.sdk.api 2 | 3 | import android.net.Uri 4 | import com.google.gson.Gson 5 | import io.reactivex.Single 6 | import io.reactivex.schedulers.Schedulers 7 | import retrofit2.HttpException 8 | import ru.cloudpayments.sdk.api.models.* 9 | import java.net.URLDecoder 10 | import javax.inject.Inject 11 | 12 | class CloudpaymentsApi @Inject constructor(private val apiService: CloudpaymentsApiService) { 13 | companion object { 14 | private const val THREE_DS_SUCCESS_URL = "https://api.cloudpayments.ru/threeds/success" 15 | private const val THREE_DS_FAIL_URL = "https://api.cloudpayments.ru/threeds/fail" 16 | } 17 | 18 | fun getPublicKey(): Single { 19 | return apiService.getPublicKey() 20 | .subscribeOn(Schedulers.io()) 21 | } 22 | 23 | fun getMerchantConfiguration(publicId: String): Single { 24 | return apiService.getMerchantConfiguration(publicId) 25 | .subscribeOn(Schedulers.io()) 26 | } 27 | 28 | fun charge(requestBody: PaymentRequestBody): Single { 29 | return apiService.charge(requestBody) 30 | .subscribeOn(Schedulers.io()) 31 | } 32 | 33 | fun auth(requestBody: PaymentRequestBody): Single { 34 | return apiService.auth(requestBody) 35 | .subscribeOn(Schedulers.io()) 36 | } 37 | 38 | fun postThreeDs(transactionId: String, threeDsCallbackId: String, paRes: String): Single { 39 | val md = ThreeDsMdData(transactionId = transactionId, threeDsCallbackId = threeDsCallbackId, successURL = THREE_DS_SUCCESS_URL, failURL = THREE_DS_FAIL_URL) 40 | val mdString = Gson().toJson(md) 41 | return apiService.postThreeDs(ThreeDsRequestBody(md = mdString, paRes = paRes)) 42 | .subscribeOn(Schedulers.io()) 43 | .map { CloudpaymentsThreeDsResponse(true, "", 0) } 44 | .onErrorReturn { 45 | val response: CloudpaymentsThreeDsResponse = if (it is HttpException && it.response()?.raw()!!.isRedirect) { 46 | val url = it.response()?.raw()?.header("Location") 47 | when { 48 | url?.startsWith(THREE_DS_FAIL_URL) == true -> { 49 | val uri = Uri.parse(url) 50 | val cardholderMessage = uri.getQueryParameter("CardHolderMessage") 51 | val reasonCode = uri.getQueryParameter("ReasonCode")?.toIntOrNull() 52 | val message = if (cardholderMessage != null) URLDecoder.decode(cardholderMessage, "utf-8") else "" 53 | CloudpaymentsThreeDsResponse(false, message, reasonCode) 54 | } 55 | url?.startsWith(THREE_DS_SUCCESS_URL) == true -> CloudpaymentsThreeDsResponse(true, null, 0) 56 | else -> CloudpaymentsThreeDsResponse(false, null, null) 57 | } 58 | } else { 59 | CloudpaymentsThreeDsResponse(true, null, 0) 60 | } 61 | 62 | response 63 | } 64 | } 65 | 66 | fun getTinkoffPayQrLink(requestBody: TinkoffPayQrLinkBody): Single { 67 | return apiService.getTinkoffPayQrLink(requestBody) 68 | .subscribeOn(Schedulers.io()) 69 | } 70 | 71 | fun qrLinkStatusWait(requestBody: QrLinkStatusWaitBody): Single { 72 | return apiService.qrLinkStatusWait(requestBody) 73 | .subscribeOn(Schedulers.io()) 74 | } 75 | 76 | fun getBinInfo(firstSixDigits: String): Single = 77 | if (firstSixDigits.length < 6) { 78 | Single.error(CloudpaymentsTransactionError("You must specify the first 6 digits of the card number")) 79 | } else { 80 | val firstSix = firstSixDigits.subSequence(0, 6).toString() 81 | apiService.getBinInfo(firstSix) 82 | .subscribeOn(Schedulers.io()) 83 | .map { it.binInfo ?: CloudpaymentsBinInfo("", "") } 84 | .onErrorReturn { CloudpaymentsBinInfo("", "") } 85 | } 86 | } -------------------------------------------------------------------------------- /sdk/src/main/java/ru/cloudpayments/sdk/api/CloudpaymentsApiService.kt: -------------------------------------------------------------------------------- 1 | package ru.cloudpayments.sdk.api 2 | 3 | import io.reactivex.Single 4 | import retrofit2.http.Body 5 | import retrofit2.http.GET 6 | import retrofit2.http.POST 7 | import retrofit2.http.Path 8 | import retrofit2.http.Query 9 | import ru.cloudpayments.sdk.api.models.CloudpaymentsBinInfoResponse 10 | import ru.cloudpayments.sdk.api.models.CloudpaymentsGetTinkoffPayQrLinkResponse 11 | import ru.cloudpayments.sdk.api.models.CloudpaymentsMerchantConfigurationResponse 12 | import ru.cloudpayments.sdk.api.models.CloudpaymentsPublicKeyResponse 13 | import ru.cloudpayments.sdk.api.models.PaymentRequestBody 14 | import ru.cloudpayments.sdk.api.models.ThreeDsRequestBody 15 | import ru.cloudpayments.sdk.api.models.CloudpaymentsTransactionResponse 16 | import ru.cloudpayments.sdk.api.models.QrLinkStatusWaitBody 17 | import ru.cloudpayments.sdk.api.models.QrLinkStatusWaitResponse 18 | import ru.cloudpayments.sdk.api.models.TinkoffPayQrLinkBody 19 | 20 | interface CloudpaymentsApiService { 21 | @POST("payments/cards/charge") 22 | fun charge(@Body body: PaymentRequestBody): Single 23 | 24 | @POST("payments/cards/auth") 25 | fun auth(@Body body: PaymentRequestBody): Single 26 | 27 | @POST("payments/ThreeDSCallback") 28 | fun postThreeDs(@Body body: ThreeDsRequestBody): Single 29 | 30 | @GET("bins/info/{firstSixDigits}") 31 | fun getBinInfo(@Path("firstSixDigits") firstSixDigits: String): Single 32 | 33 | @GET("payments/publickey") 34 | fun getPublicKey(): Single 35 | 36 | @GET("merchant/configuration") 37 | fun getMerchantConfiguration(@Query("terminalPublicId") publicId: String): Single 38 | 39 | @POST("payments/qr/tinkoffpay/link") 40 | fun getTinkoffPayQrLink(@Body body: TinkoffPayQrLinkBody): Single 41 | 42 | @POST("payments/qr/status/wait") 43 | fun qrLinkStatusWait(@Body body: QrLinkStatusWaitBody): Single 44 | } -------------------------------------------------------------------------------- /sdk/src/main/java/ru/cloudpayments/sdk/api/models/CardCryptogramPacket.kt: -------------------------------------------------------------------------------- 1 | package ru.cloudpayments.sdk.api.models 2 | 3 | import android.os.Parcelable 4 | import com.google.gson.annotations.SerializedName 5 | import kotlinx.android.parcel.Parcelize 6 | 7 | @Parcelize 8 | data class CardCryptogramPacket( 9 | @SerializedName("Type") var type: String? = "CloudCard", 10 | @SerializedName("BrowserInfoBase64") var browserInfoBase64: String? = "", 11 | @SerializedName("CardInfo") var cardInfo: CardInfo?, 12 | @SerializedName("KeyVersion") var keyVersion: String?, 13 | @SerializedName("Format") var format: Int? = 1, 14 | @SerializedName("Value") var value: String?) : Parcelable 15 | 16 | @Parcelize 17 | data class CardInfo( 18 | @SerializedName("FirstSixDigits") var firstSixDigits: String?, 19 | @SerializedName("LastFourDigits") var lastFourDigits: String?, 20 | @SerializedName("ExpDateYear") var expDateYear: String?, 21 | @SerializedName("ExpDateMonth") var expDateMonth: String?) :Parcelable -------------------------------------------------------------------------------- /sdk/src/main/java/ru/cloudpayments/sdk/api/models/CloudpaymentsBinInfoResponse.kt: -------------------------------------------------------------------------------- 1 | package ru.cloudpayments.sdk.api.models 2 | 3 | import com.google.gson.annotations.SerializedName 4 | 5 | data class CloudpaymentsBinInfoResponse( 6 | @SerializedName("Success") val success: Boolean?, 7 | @SerializedName("Message") val message: String?, 8 | @SerializedName("Model") val binInfo: CloudpaymentsBinInfo?) 9 | 10 | data class CloudpaymentsBinInfo( 11 | @SerializedName("LogoUrl") val logoUrl: String?, 12 | @SerializedName("BankName") val bankName: String?) -------------------------------------------------------------------------------- /sdk/src/main/java/ru/cloudpayments/sdk/api/models/CloudpaymentsGetTinkoffPayQrLinkResponse.kt: -------------------------------------------------------------------------------- 1 | package ru.cloudpayments.sdk.api.models 2 | 3 | import com.google.gson.annotations.SerializedName 4 | import io.reactivex.Observable 5 | 6 | data class CloudpaymentsGetTinkoffPayQrLinkResponse( 7 | @SerializedName("Success") val success: Boolean?, 8 | @SerializedName("Message") val message: String?, 9 | @SerializedName("Model") val transaction: CloudpaymentsTinkoffPayQrLinkTransaction?) { 10 | fun handleError(): Observable { 11 | return if (success == true ) { 12 | Observable.just(this) 13 | } else { 14 | Observable.error(CloudpaymentsTransactionError(message ?: "")) 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /sdk/src/main/java/ru/cloudpayments/sdk/api/models/CloudpaymentsMerchantConfigurationResponse.kt: -------------------------------------------------------------------------------- 1 | package ru.cloudpayments.sdk.api.models 2 | 3 | import com.google.gson.annotations.SerializedName 4 | 5 | data class CloudpaymentsMerchantConfigurationResponse( 6 | @SerializedName("Success") val success: Boolean?, 7 | @SerializedName("Message") val message: String?, 8 | @SerializedName("Model") val model: MerchantConfiguration? 9 | ) 10 | 11 | data class MerchantConfiguration( 12 | @SerializedName("ExternalPaymentMethods") val externalPaymentMethods: ArrayList?, 13 | @SerializedName("Features") val features: Features? 14 | ) 15 | 16 | data class ExternalPaymentMethods( 17 | @SerializedName("Type") val type: Int?, 18 | @SerializedName("Enabled") val enabled: Boolean? 19 | ) 20 | 21 | data class Features( 22 | @SerializedName("IsSaveCard") val isSaveCard: Int? 23 | ) -------------------------------------------------------------------------------- /sdk/src/main/java/ru/cloudpayments/sdk/api/models/CloudpaymentsPublicKeyResponse.kt: -------------------------------------------------------------------------------- 1 | package ru.cloudpayments.sdk.api.models 2 | 3 | import com.google.gson.annotations.SerializedName 4 | 5 | data class CloudpaymentsPublicKeyResponse( 6 | @SerializedName("Pem") val pem: String?, 7 | @SerializedName("Version") val version: Int?) 8 | 9 | -------------------------------------------------------------------------------- /sdk/src/main/java/ru/cloudpayments/sdk/api/models/CloudpaymentsThreeDsResponse.kt: -------------------------------------------------------------------------------- 1 | package ru.cloudpayments.sdk.api.models 2 | 3 | data class CloudpaymentsThreeDsResponse(val success: Boolean, val message: String?, val reasonCode: Int?) -------------------------------------------------------------------------------- /sdk/src/main/java/ru/cloudpayments/sdk/api/models/CloudpaymentsTinkoffPayQrLinkTransaction.kt: -------------------------------------------------------------------------------- 1 | package ru.cloudpayments.sdk.api.models 2 | 3 | import com.google.gson.annotations.SerializedName 4 | 5 | data class CloudpaymentsTinkoffPayQrLinkTransaction( 6 | @SerializedName("TransactionId") val transactionId: Int?, 7 | @SerializedName("ProviderQrId") val providerQrId: String?, 8 | @SerializedName("QrUrl") val qrUrl: String?) -------------------------------------------------------------------------------- /sdk/src/main/java/ru/cloudpayments/sdk/api/models/CloudpaymentsTransaction.kt: -------------------------------------------------------------------------------- 1 | package ru.cloudpayments.sdk.api.models 2 | 3 | import com.google.gson.annotations.SerializedName 4 | 5 | data class CloudpaymentsTransaction( 6 | @SerializedName("TransactionId") val transactionId: Int?, 7 | @SerializedName("ReasonCode") val reasonCode: Int?, 8 | @SerializedName("CardHolderMessage") val cardHolderMessage: String?, 9 | @SerializedName("PaReq") val paReq: String?, 10 | @SerializedName("AcsUrl") val acsUrl: String?, 11 | @SerializedName("ThreeDsCallbackId") val threeDsCallbackId: String?) -------------------------------------------------------------------------------- /sdk/src/main/java/ru/cloudpayments/sdk/api/models/CloudpaymentsTransactionError.kt: -------------------------------------------------------------------------------- 1 | package ru.cloudpayments.sdk.api.models 2 | 3 | class CloudpaymentsTransactionError(override val message: String): Throwable() -------------------------------------------------------------------------------- /sdk/src/main/java/ru/cloudpayments/sdk/api/models/CloudpaymentsTransactionResponse.kt: -------------------------------------------------------------------------------- 1 | package ru.cloudpayments.sdk.api.models 2 | 3 | import com.google.gson.annotations.SerializedName 4 | import io.reactivex.Observable 5 | 6 | data class CloudpaymentsTransactionResponse( 7 | @SerializedName("Success") val success: Boolean?, 8 | @SerializedName("Message") val message: String?, 9 | @SerializedName("Model") val transaction: CloudpaymentsTransaction?) { 10 | fun handleError(): Observable { 11 | return if (success == true || (!transaction?.acsUrl.isNullOrEmpty() && !transaction?.paReq.isNullOrEmpty())){ 12 | Observable.just(this) 13 | } else { 14 | Observable.error(CloudpaymentsTransactionError(message ?: transaction?.cardHolderMessage.orEmpty())) 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /sdk/src/main/java/ru/cloudpayments/sdk/api/models/PaymentDataPayer.kt: -------------------------------------------------------------------------------- 1 | package ru.cloudpayments.sdk.api.models 2 | 3 | import android.os.Parcelable 4 | import com.google.gson.annotations.SerializedName 5 | import kotlinx.android.parcel.Parcelize 6 | 7 | @Parcelize 8 | data class PaymentDataPayer( 9 | @SerializedName("FirstName") var firstName: String? = null, 10 | @SerializedName("LastName") var lastName: String? = null, 11 | @SerializedName("MiddleName") var middleName: String? = null, 12 | @SerializedName("Birth") var birthDay: String? = null, 13 | @SerializedName("Address") var address: String? = null, 14 | @SerializedName("Street") var street: String? = null, 15 | @SerializedName("City") var city: String? = null, 16 | @SerializedName("Country") var country: String? = null, 17 | @SerializedName("Phone") var phone: String? = null, 18 | @SerializedName("Postcode") var postcode: String? = null) : Parcelable 19 | -------------------------------------------------------------------------------- /sdk/src/main/java/ru/cloudpayments/sdk/api/models/PaymentRequestBody.kt: -------------------------------------------------------------------------------- 1 | package ru.cloudpayments.sdk.api.models 2 | 3 | import com.google.gson.annotations.SerializedName 4 | 5 | data class PaymentRequestBody( 6 | @SerializedName("Amount") val amount: String, // Сумма (Обязательный) 7 | @SerializedName("Currency") val currency: String, // Валюта (Обязательный) 8 | @SerializedName("IpAddress") val ipAddress: String, // IP адрес плательщика (Обязательный) 9 | @SerializedName("Name") val name: String, // Имя держателя карты в латинице (Обязательный для всех платежей кроме Apple Pay и Google Pay) 10 | @SerializedName("CardCryptogramPacket") val cryptogram: String, // Криптограмма платежных данных (Обязательный) 11 | @SerializedName("InvoiceId") val invoiceId: String? = null, // Номер счета или заказа в вашей системе (необязательный) 12 | @SerializedName("Description") val description: String? = null, // Описание оплаты в свободной форме (необязательный) 13 | @SerializedName("AccountId") val accountId: String? = null, // Идентификатор пользователя в вашей системе (необязательный) 14 | @SerializedName("Email") val email: String? = null, // E-mail, на который будет отправлена квитанция об оплате) 15 | @SerializedName("Payer") val payer: PaymentDataPayer? = null, // Доп. поле, куда передается информация о плательщике. Используйте следующие параметры: FirstName, LastName, MiddleName, Birth, Street, Address, City, Country, Phone, Postcode 16 | @SerializedName("JsonData") val jsonData: String? = null, //"{\"age\":27,\"name\":\"Ivan\",\"phone\":\"+79998881122\"}" Любые другие данные, которые будут связаны с транзакцией (необязательный) 17 | @SerializedName("scenario") val scenario: Int = 7) -------------------------------------------------------------------------------- /sdk/src/main/java/ru/cloudpayments/sdk/api/models/QrLinkStatusWait.kt: -------------------------------------------------------------------------------- 1 | package ru.cloudpayments.sdk.api.models 2 | 3 | import com.google.gson.annotations.SerializedName 4 | 5 | data class QrLinkStatusWait( 6 | @SerializedName("TransactionId") val transactionId: Int?, 7 | @SerializedName("Status") val status: String?, 8 | @SerializedName("StatusCode") val statusCode: String?) 9 | 10 | 11 | -------------------------------------------------------------------------------- /sdk/src/main/java/ru/cloudpayments/sdk/api/models/QrLinkStatusWaitBody.kt: -------------------------------------------------------------------------------- 1 | package ru.cloudpayments.sdk.api.models 2 | 3 | import com.google.gson.annotations.SerializedName 4 | 5 | data class QrLinkStatusWaitBody( 6 | @SerializedName("TransactionId") val transactionId: Int) -------------------------------------------------------------------------------- /sdk/src/main/java/ru/cloudpayments/sdk/api/models/QrLinkStatusWaitResponse.kt: -------------------------------------------------------------------------------- 1 | package ru.cloudpayments.sdk.api.models 2 | 3 | import com.google.gson.annotations.SerializedName 4 | import io.reactivex.Observable 5 | 6 | data class QrLinkStatusWaitResponse( 7 | @SerializedName("Success") val success: Boolean?, 8 | @SerializedName("Message") val message: String?, 9 | @SerializedName("Model") val transaction: QrLinkStatusWait?) { 10 | fun handleError(): Observable { 11 | return if (success == true ){ 12 | Observable.just(this) 13 | } else { 14 | Observable.error(CloudpaymentsTransactionError(message ?: "")) 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /sdk/src/main/java/ru/cloudpayments/sdk/api/models/ThreeDsMdData.kt: -------------------------------------------------------------------------------- 1 | package ru.cloudpayments.sdk.api.models 2 | 3 | import com.google.gson.annotations.SerializedName 4 | import java.io.Serializable 5 | 6 | data class ThreeDsMdData( 7 | @SerializedName("TransactionId") val transactionId: String, 8 | @SerializedName("ThreeDsCallbackId") val threeDsCallbackId: String, 9 | @SerializedName("SuccessUrl") val successURL: String, 10 | @SerializedName("FailUrl") val failURL: String): Serializable -------------------------------------------------------------------------------- /sdk/src/main/java/ru/cloudpayments/sdk/api/models/ThreeDsRequestBody.kt: -------------------------------------------------------------------------------- 1 | package ru.cloudpayments.sdk.api.models 2 | 3 | import com.google.gson.annotations.SerializedName 4 | 5 | data class ThreeDsRequestBody( 6 | @SerializedName("MD") val md: String, 7 | @SerializedName("PaRes") val paRes: String) -------------------------------------------------------------------------------- /sdk/src/main/java/ru/cloudpayments/sdk/api/models/TinkoffPayQrLinkBody.kt: -------------------------------------------------------------------------------- 1 | package ru.cloudpayments.sdk.api.models 2 | 3 | import com.google.gson.annotations.SerializedName 4 | 5 | data class TinkoffPayQrLinkBody( 6 | @SerializedName("Webview") val webView: Boolean = true, // Мобильное устройство 7 | @SerializedName("Device") val device: String = "MobileApp", // Вызов из мобильных приложений 8 | @SerializedName("Amount") val amount: String, // Сумма 9 | @SerializedName("Currency") val currency: String, // Валюта 10 | @SerializedName("Description") val description: String? = null, // Описание платежа 11 | @SerializedName("AccountId") val accountId: String? = null, // Identity плательщика в системе мерчанта 12 | @SerializedName("Email") val email: String? = null, // E-mail плательщика 13 | @SerializedName("JsonData") val jsonData: String? = null, // Произвольные данные мерчанта в формате JSON 14 | @SerializedName("InvoiceId") val invoiceId: String? = null, // id заказа в системе мерчанта 15 | @SerializedName("Scheme") val scheme: String, // charge - одностадийная оплата, auth - двухстадийная оплата (Scheme":"0") 16 | @SerializedName("TtlMinutes") val ttlMinutes: Int = 30, // Время жизни Qr 17 | @SerializedName("SuccessRedirectUrl") val successRedirectUrl: String = "https://cp.ru", // Url успешной оплаты (мерчанта) 18 | @SerializedName("FailRedirectUrl") val failRedirectUrl: String = "https://cp.ru", // Url неуспешной оплаты (мерчанта) 19 | @SerializedName("SaveCard") var saveCard: Boolean? = null) -------------------------------------------------------------------------------- /sdk/src/main/java/ru/cloudpayments/sdk/card/CardType.kt: -------------------------------------------------------------------------------- 1 | package ru.cloudpayments.sdk.card 2 | 3 | import ru.cloudpayments.sdk.R 4 | import java.util.* 5 | 6 | enum class CardType { 7 | UNKNOWN, 8 | VISA, 9 | MASTER_CARD, 10 | MAESTRO, 11 | MIR, 12 | JCB; 13 | 14 | companion object { 15 | fun fromString(value: String): CardType = when(value.toLowerCase(Locale.getDefault())){ 16 | "visa" -> VISA 17 | "mastercard" -> MASTER_CARD 18 | "maestro" -> MAESTRO 19 | "mir" -> MIR 20 | "jcb" -> JCB 21 | else -> UNKNOWN 22 | } 23 | 24 | fun getType(cardNumber: String?): CardType { 25 | if (cardNumber == null || cardNumber.isEmpty()) return UNKNOWN 26 | val first = Integer.valueOf(cardNumber.substring(0, 1)) 27 | if (first == 4) return VISA 28 | if (first == 6) return MAESTRO 29 | if (cardNumber.length < 2) return UNKNOWN 30 | val firstTwo = Integer.valueOf(cardNumber.substring(0, 2)) 31 | if (firstTwo == 35) return JCB 32 | if (firstTwo == 50 || (firstTwo in 56..58)) return MAESTRO 33 | if (firstTwo in 51..55) return MASTER_CARD 34 | if (cardNumber.length < 4) return UNKNOWN 35 | val firstFour = Integer.valueOf(cardNumber.substring(0, 4)) 36 | if (firstFour in 2200..2204) return MIR 37 | return if (firstFour in 2221..2720) MASTER_CARD else UNKNOWN 38 | } 39 | } 40 | 41 | override fun toString(): String { 42 | return when (this) { 43 | VISA -> "Visa" 44 | MASTER_CARD -> "MasterCard" 45 | MAESTRO -> "Maestro" 46 | MIR -> "MIR" 47 | JCB -> "JCB" 48 | else -> "Unknown" 49 | } 50 | } 51 | 52 | fun getIconRes(): Int? = when (this) { 53 | VISA -> R.drawable.cpsdk_ic_ps_visa 54 | MASTER_CARD -> R.drawable.cpsdk_ic_ps_mastercard 55 | MAESTRO -> R.drawable.cpsdk_ic_ps_maestro 56 | MIR -> R.drawable.cpsdk_ic_ps_mir 57 | JCB -> R.drawable.cpsdk_ic_ps_jcb 58 | else -> null 59 | } 60 | } -------------------------------------------------------------------------------- /sdk/src/main/java/ru/cloudpayments/sdk/configuration/CloudPaymentsIntentSender.kt: -------------------------------------------------------------------------------- 1 | package ru.cloudpayments.sdk.configuration 2 | 3 | import android.app.Activity 4 | import android.content.Context 5 | import android.content.Intent 6 | import androidx.activity.result.contract.ActivityResultContract 7 | import ru.cloudpayments.sdk.models.Transaction 8 | 9 | internal class CloudPaymentsIntentSender : ActivityResultContract() { 10 | override fun createIntent(context: Context, input: PaymentConfiguration): Intent { 11 | return CloudpaymentsSDK.getInstance().getStartIntent(context, input) 12 | } 13 | 14 | override fun parseResult(resultCode: Int, intent: Intent?): Transaction { 15 | if (resultCode == Activity.RESULT_OK) { 16 | val id = intent?.getIntExtra(CloudpaymentsSDK.IntentKeys.TransactionId.name, 0) ?: 0 17 | val status = intent?.getSerializableExtra(CloudpaymentsSDK.IntentKeys.TransactionStatus.name) as? CloudpaymentsSDK.TransactionStatus 18 | val reasonCode = intent?.getIntExtra(CloudpaymentsSDK.IntentKeys.TransactionReasonCode.name, 0) ?: 0 19 | 20 | return Transaction(id, status, reasonCode) 21 | } 22 | 23 | return Transaction(0, null, 0) 24 | } 25 | } -------------------------------------------------------------------------------- /sdk/src/main/java/ru/cloudpayments/sdk/configuration/CloudpaymentsSDK.kt: -------------------------------------------------------------------------------- 1 | package ru.cloudpayments.sdk.configuration 2 | 3 | import android.content.Context 4 | import android.content.Intent 5 | import androidx.activity.result.ActivityResultLauncher 6 | import androidx.appcompat.app.AppCompatActivity 7 | import androidx.fragment.app.Fragment 8 | import androidx.fragment.app.FragmentActivity 9 | import com.yandex.pay.core.YandexPayEnvironment 10 | import com.yandex.pay.core.YandexPayLib 11 | import com.yandex.pay.core.YandexPayLibConfig 12 | import com.yandex.pay.core.YandexPayLocale 13 | import com.yandex.pay.core.data.Merchant 14 | import com.yandex.pay.core.data.MerchantId 15 | import okhttp3.OkHttpClient 16 | import okhttp3.logging.HttpLoggingInterceptor 17 | import retrofit2.Retrofit 18 | import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory 19 | import retrofit2.converter.gson.GsonConverterFactory 20 | import ru.cloudpayments.sdk.Constants 21 | import ru.cloudpayments.sdk.api.AuthenticationInterceptor 22 | import ru.cloudpayments.sdk.api.CloudpaymentsApiService 23 | import ru.cloudpayments.sdk.api.CloudpaymentsApi 24 | import ru.cloudpayments.sdk.models.Transaction 25 | import ru.cloudpayments.sdk.ui.PaymentActivity 26 | import java.util.concurrent.TimeUnit 27 | 28 | interface CloudpaymentsSDK { 29 | fun start(configuration: PaymentConfiguration, from: AppCompatActivity, requestCode: Int) 30 | fun launcher(from: AppCompatActivity, result: (Transaction) -> Unit) : ActivityResultLauncher 31 | fun launcher(from: FragmentActivity, result: (Transaction) -> Unit) : ActivityResultLauncher 32 | fun launcher(from: Fragment, result: (Transaction) -> Unit) : ActivityResultLauncher 33 | 34 | fun getStartIntent(context: Context, configuration: PaymentConfiguration): Intent 35 | 36 | enum class TransactionStatus { 37 | Succeeded, 38 | Failed; 39 | } 40 | enum class IntentKeys { 41 | TransactionId, 42 | TransactionStatus, 43 | TransactionReasonCode; 44 | } 45 | 46 | companion object { 47 | 48 | fun initialize(context: Context, yandexPayAppId: String, yandexPaySandboxMode: Boolean) { 49 | if (YandexPayLib.isSupported) { 50 | YandexPayLib.initialize( 51 | context = context, 52 | config = YandexPayLibConfig( 53 | merchantDetails = Merchant( 54 | id = MerchantId.from(yandexPayAppId), 55 | name = "Cloud", 56 | url = "https://cp.ru/", 57 | ), 58 | environment = if (yandexPaySandboxMode) YandexPayEnvironment.SANDBOX else YandexPayEnvironment.PROD, 59 | locale = YandexPayLocale.SYSTEM, 60 | logging = false 61 | ) 62 | ) 63 | } 64 | } 65 | 66 | fun getInstance(): CloudpaymentsSDK { 67 | return CloudpaymentsSDKImpl() 68 | } 69 | 70 | fun createApi(publicId: String) = CloudpaymentsApi(createService(publicId)) 71 | 72 | private fun createService(publicId: String): CloudpaymentsApiService { 73 | val retrofit = Retrofit.Builder() 74 | .baseUrl(Constants.baseApiUrl) 75 | .addConverterFactory(GsonConverterFactory.create()) 76 | .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) 77 | .client(createClient(publicId)) 78 | .build() 79 | 80 | return retrofit.create(CloudpaymentsApiService::class.java) 81 | } 82 | 83 | private fun createClient(publicId: String?): OkHttpClient { 84 | val okHttpClientBuilder = OkHttpClient.Builder() 85 | .addInterceptor(HttpLoggingInterceptor() 86 | .setLevel(HttpLoggingInterceptor.Level.BODY)) 87 | val client = okHttpClientBuilder 88 | .connectTimeout(20, TimeUnit.SECONDS) 89 | .readTimeout(20, TimeUnit.SECONDS) 90 | .followRedirects(false) 91 | 92 | if (publicId != null){ 93 | client.addInterceptor(AuthenticationInterceptor(publicId)) 94 | } 95 | 96 | return client.build() 97 | } 98 | } 99 | } 100 | 101 | internal class CloudpaymentsSDKImpl: CloudpaymentsSDK { 102 | override fun start(configuration: PaymentConfiguration, from: AppCompatActivity, requestCode: Int) { 103 | from.startActivityForResult(this.getStartIntent(from, configuration), requestCode) 104 | } 105 | 106 | override fun launcher( 107 | from: AppCompatActivity, 108 | result: (Transaction) -> Unit): ActivityResultLauncher { 109 | return from.registerForActivityResult(CloudPaymentsIntentSender(), result) 110 | } 111 | 112 | override fun launcher( 113 | from: FragmentActivity, 114 | result: (Transaction) -> Unit): ActivityResultLauncher { 115 | return from.registerForActivityResult(CloudPaymentsIntentSender(), result) 116 | } 117 | 118 | override fun launcher( 119 | from: Fragment, 120 | result: (Transaction) -> Unit 121 | ): ActivityResultLauncher { 122 | return from.registerForActivityResult(CloudPaymentsIntentSender(), result) 123 | } 124 | 125 | override fun getStartIntent(context: Context, configuration: PaymentConfiguration): Intent { 126 | return PaymentActivity.getStartIntent(context, configuration) 127 | } 128 | } -------------------------------------------------------------------------------- /sdk/src/main/java/ru/cloudpayments/sdk/configuration/PaymentConfiguration.kt: -------------------------------------------------------------------------------- 1 | package ru.cloudpayments.sdk.configuration 2 | 3 | import android.os.Parcelable 4 | import kotlinx.android.parcel.Parcelize 5 | import ru.cloudpayments.sdk.scanner.CardScanner 6 | 7 | @Parcelize 8 | data class PaymentConfiguration(val publicId: String, 9 | val paymentData: PaymentData, 10 | val scanner: CardScanner?, 11 | val requireEmail: Boolean = false, 12 | val useDualMessagePayment: Boolean = false, 13 | val disableGPay: Boolean = false, 14 | val disableYandexPay: Boolean = false, 15 | val yandexPayMerchantID: String = "", 16 | val apiUrl: String = ""): Parcelable -------------------------------------------------------------------------------- /sdk/src/main/java/ru/cloudpayments/sdk/configuration/PaymentData.kt: -------------------------------------------------------------------------------- 1 | package ru.cloudpayments.sdk.configuration 2 | 3 | import android.os.Parcelable 4 | import android.util.Log 5 | import com.google.gson.GsonBuilder 6 | import com.google.gson.JsonSyntaxException 7 | import com.google.gson.annotations.SerializedName 8 | import kotlinx.android.parcel.Parcelize 9 | import ru.cloudpayments.sdk.Constants 10 | import ru.cloudpayments.sdk.api.models.PaymentDataPayer 11 | import ru.cloudpayments.sdk.util.TAG 12 | 13 | @Parcelize 14 | class PaymentData( 15 | val amount: String, 16 | var currency: String = "RUB", 17 | val invoiceId: String? = null, 18 | val description: String? = null, 19 | val accountId: String? = null, 20 | var email: String? = null, 21 | val payer: PaymentDataPayer? = null, 22 | val jsonData: String? = null 23 | ) : Parcelable { 24 | 25 | fun jsonDataHasRecurrent(): Boolean { 26 | 27 | if (!jsonData.isNullOrEmpty()) { 28 | val gson = GsonBuilder() 29 | .setLenient() 30 | .create() 31 | 32 | try { 33 | val cpJsonData = gson.fromJson(jsonData, CpJsonData::class.java) 34 | cpJsonData.cloudPayments?.recurrent?.interval?.let { 35 | return true 36 | } 37 | } catch (e: JsonSyntaxException) { 38 | Log.e(TAG, "JsonData syntax error") 39 | } 40 | } 41 | return false 42 | } 43 | } 44 | 45 | data class CpJsonData( 46 | @SerializedName("cloudPayments") val cloudPayments: CloudPaymentsJsonData? 47 | ) 48 | 49 | data class CloudPaymentsJsonData( 50 | @SerializedName("recurrent") val recurrent: CloudPaymentsRecurrentJsonData? 51 | ) 52 | 53 | data class CloudPaymentsRecurrentJsonData( 54 | @SerializedName("interval") val interval: String?, 55 | @SerializedName("period") val period: String?, 56 | @SerializedName("amount") val amount: String? 57 | ) 58 | -------------------------------------------------------------------------------- /sdk/src/main/java/ru/cloudpayments/sdk/dagger2/CloudpaymentsModule.kt: -------------------------------------------------------------------------------- 1 | package ru.cloudpayments.sdk.dagger2 2 | 3 | import dagger.Component 4 | import dagger.Module 5 | import dagger.Provides 6 | import okhttp3.OkHttpClient 7 | import okhttp3.logging.HttpLoggingInterceptor 8 | import retrofit2.Retrofit 9 | import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory 10 | import retrofit2.converter.gson.GsonConverterFactory 11 | import ru.cloudpayments.sdk.Constants 12 | import ru.cloudpayments.sdk.api.AuthenticationInterceptor 13 | import ru.cloudpayments.sdk.api.CloudpaymentsApiService 14 | import ru.cloudpayments.sdk.api.CloudpaymentsApi 15 | import ru.cloudpayments.sdk.viewmodel.PaymentCardViewModel 16 | import ru.cloudpayments.sdk.viewmodel.PaymentOptionsViewModel 17 | import ru.cloudpayments.sdk.viewmodel.PaymentProcessViewModel 18 | import java.util.concurrent.TimeUnit 19 | import javax.inject.Singleton 20 | 21 | @Module 22 | class CloudpaymentsModule { 23 | @Provides 24 | @Singleton 25 | fun provideRepository(apiService: CloudpaymentsApiService) 26 | = CloudpaymentsApi(apiService) 27 | } 28 | 29 | @Module 30 | class CloudpaymentsNetModule(private val publicId: String, private var apiUrl: String = Constants.baseApiUrl) { 31 | @Provides 32 | @Singleton 33 | fun providesHttpLoggingInterceptor(): HttpLoggingInterceptor = HttpLoggingInterceptor() 34 | .setLevel(HttpLoggingInterceptor.Level.BODY) 35 | 36 | @Provides 37 | @Singleton 38 | fun providesAuthenticationInterceptor(): AuthenticationInterceptor 39 | = AuthenticationInterceptor(publicId) 40 | 41 | @Provides 42 | @Singleton 43 | fun provideOkHttpClientBuilder(loggingInterceptor: HttpLoggingInterceptor): OkHttpClient.Builder 44 | = OkHttpClient.Builder() 45 | .addInterceptor(loggingInterceptor) 46 | 47 | @Provides 48 | @Singleton 49 | fun provideApiService(okHttpClientBuilder: OkHttpClient.Builder, 50 | authenticationInterceptor: AuthenticationInterceptor): CloudpaymentsApiService { 51 | val client = okHttpClientBuilder 52 | .addInterceptor(authenticationInterceptor) 53 | .connectTimeout(60, TimeUnit.SECONDS) 54 | .readTimeout(60, TimeUnit.SECONDS) 55 | .followRedirects(false) 56 | .build() 57 | 58 | if (apiUrl.isEmpty()) 59 | apiUrl = Constants.baseApiUrl 60 | 61 | val retrofit = Retrofit.Builder() 62 | .baseUrl(apiUrl) 63 | .addConverterFactory(GsonConverterFactory.create()) 64 | .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) 65 | .client(client) 66 | .build() 67 | 68 | return retrofit.create(CloudpaymentsApiService::class.java) 69 | } 70 | } 71 | 72 | @Singleton 73 | @Component(modules = [CloudpaymentsModule::class, CloudpaymentsNetModule::class]) 74 | internal interface CloudpaymentsComponent { 75 | fun inject(optionsViewModel: PaymentOptionsViewModel) 76 | fun inject(cardViewModel: PaymentCardViewModel) 77 | fun inject(processViewModel: PaymentProcessViewModel) 78 | } 79 | -------------------------------------------------------------------------------- /sdk/src/main/java/ru/cloudpayments/sdk/models/ApiError.kt: -------------------------------------------------------------------------------- 1 | package ru.cloudpayments.sdk.models 2 | 3 | import android.content.Context 4 | import ru.cloudpayments.sdk.R 5 | 6 | class ApiError { 7 | 8 | companion object { 9 | 10 | fun getFullErrorDescription(context: Context, code: String): String { 11 | var error = getErrorDescription(context, code) 12 | var errorExtra = getErrorDescriptionExtra(context, code) 13 | return "$error. $errorExtra" 14 | } 15 | 16 | fun getErrorDescription(context: Context, code: String): String { 17 | return when(code) { 18 | "3001" -> context.getString(R.string.cpsdk_error_3001) 19 | "3002" -> context.getString(R.string.cpsdk_error_3002) 20 | "3003" -> context.getString(R.string.cpsdk_error_3003) 21 | "3004" -> context.getString(R.string.cpsdk_error_3004) 22 | "3005" -> context.getString(R.string.cpsdk_error_3005) 23 | "3006" -> context.getString(R.string.cpsdk_error_3006) 24 | "3007" -> context.getString(R.string.cpsdk_error_3007) 25 | "3008" -> context.getString(R.string.cpsdk_error_3008) 26 | "5001" -> context.getString(R.string.cpsdk_error_5001) 27 | "5005" -> context.getString(R.string.cpsdk_error_5005) 28 | "5006" -> context.getString(R.string.cpsdk_error_5006) 29 | "5012" -> context.getString(R.string.cpsdk_error_5012) 30 | "5013" -> context.getString(R.string.cpsdk_error_5013) 31 | "5030" -> context.getString(R.string.cpsdk_error_5030) 32 | "5031" -> context.getString(R.string.cpsdk_error_5031) 33 | "5034" -> context.getString(R.string.cpsdk_error_5034) 34 | "5041" -> context.getString(R.string.cpsdk_error_5041) 35 | "5043" -> context.getString(R.string.cpsdk_error_5043) 36 | "5051" -> context.getString(R.string.cpsdk_error_5051) 37 | "5054" -> context.getString(R.string.cpsdk_error_5054) 38 | "5057" -> context.getString(R.string.cpsdk_error_5057) 39 | "5065" -> context.getString(R.string.cpsdk_error_5065) 40 | "5082" -> context.getString(R.string.cpsdk_error_5082) 41 | "5091" -> context.getString(R.string.cpsdk_error_5091) 42 | "5092" -> context.getString(R.string.cpsdk_error_5092) 43 | "5096" -> context.getString(R.string.cpsdk_error_5096) 44 | "5204" -> context.getString(R.string.cpsdk_error_5204) 45 | "5206" -> context.getString(R.string.cpsdk_error_5206) 46 | "5207" -> context.getString(R.string.cpsdk_error_5207) 47 | "5300" -> context.getString(R.string.cpsdk_error_5300) 48 | else -> context.getString(R.string.cpsdk_error_5204) 49 | } 50 | } 51 | 52 | fun getErrorDescriptionExtra(context: Context, code: String): String { 53 | return when(code) { 54 | "3001" -> context.getString(R.string.cpsdk_error_3001_extra) 55 | "3002" -> context.getString(R.string.cpsdk_error_3002_extra) 56 | "3003" -> context.getString(R.string.cpsdk_error_3003_extra) 57 | "3004" -> context.getString(R.string.cpsdk_error_3004_extra) 58 | "3005" -> context.getString(R.string.cpsdk_error_3005_extra) 59 | "3006" -> context.getString(R.string.cpsdk_error_3006_extra) 60 | "3007" -> context.getString(R.string.cpsdk_error_3007_extra) 61 | "3008" -> context.getString(R.string.cpsdk_error_3008_extra) 62 | "5001" -> context.getString(R.string.cpsdk_error_5001_extra) 63 | "5005" -> context.getString(R.string.cpsdk_error_5005_extra) 64 | "5006" -> context.getString(R.string.cpsdk_error_5006_extra) 65 | "5012" -> context.getString(R.string.cpsdk_error_5012_extra) 66 | "5013" -> context.getString(R.string.cpsdk_error_5013_extra) 67 | "5030" -> context.getString(R.string.cpsdk_error_5030_extra) 68 | "5031" -> context.getString(R.string.cpsdk_error_5031_extra) 69 | "5034" -> context.getString(R.string.cpsdk_error_5034_extra) 70 | "5041" -> context.getString(R.string.cpsdk_error_5041_extra) 71 | "5043" -> context.getString(R.string.cpsdk_error_5043_extra) 72 | "5051" -> context.getString(R.string.cpsdk_error_5051_extra) 73 | "5054" -> context.getString(R.string.cpsdk_error_5054_extra) 74 | "5057" -> context.getString(R.string.cpsdk_error_5057_extra) 75 | "5065" -> context.getString(R.string.cpsdk_error_5065_extra) 76 | "5082" -> context.getString(R.string.cpsdk_error_5082_extra) 77 | "5091" -> context.getString(R.string.cpsdk_error_5091_extra) 78 | "5092" -> context.getString(R.string.cpsdk_error_5092_extra) 79 | "5096" -> context.getString(R.string.cpsdk_error_5096_extra) 80 | "5204" -> context.getString(R.string.cpsdk_error_5204_extra) 81 | "5206" -> context.getString(R.string.cpsdk_error_5206_extra) 82 | "5207" -> context.getString(R.string.cpsdk_error_5207_extra) 83 | "5300" -> context.getString(R.string.cpsdk_error_5300_extra) 84 | else -> context.getString(R.string.cpsdk_error_5204_extra) 85 | } 86 | } 87 | } 88 | } -------------------------------------------------------------------------------- /sdk/src/main/java/ru/cloudpayments/sdk/models/Currency.kt: -------------------------------------------------------------------------------- 1 | package ru.cloudpayments.sdk.models 2 | 3 | class Currency { 4 | 5 | companion object { 6 | 7 | val CODE_RUB = "RUB" 8 | val CODE_USD = "USD" 9 | val CODE_EUR = "EUR" 10 | val CODE_GBP = "GBP" 11 | val CODE_KZT = "KZT" 12 | val CODE_BYN = "BYN" 13 | val CODE_UAH = "UAH" 14 | val CODE_CHF = "CHF" 15 | val CODE_AZN = "AZN" 16 | val CODE_CZK = "CZK" 17 | val CODE_CAD = "CAD" 18 | val CODE_PLN = "PLN" 19 | val CODE_SEK = "SEK" 20 | val CODE_TRY = "TRY" 21 | val CODE_CNY = "CNY" 22 | val CODE_INR = "INR" 23 | val CODE_BRL = "BRL" 24 | val CODE_ZAR = "ZAR" 25 | 26 | val SYMBOL_RUB = "\u20BD" 27 | val SYMBOL_USD = "$" 28 | val SYMBOL_EUR = "€" 29 | val SYMBOL_GBP = "£" 30 | val SYMBOL_KZT = "₸" 31 | val SYMBOL_BYN = "Br" 32 | val SYMBOL_UAH = "грн" 33 | val SYMBOL_CHF = "Fr" 34 | val SYMBOL_AZN = "man" 35 | val SYMBOL_CZK = "Kč" 36 | val SYMBOL_CAD = "C$" 37 | val SYMBOL_PLN = "zł" 38 | val SYMBOL_SEK = "kr" 39 | val SYMBOL_TRY = "₺" 40 | val SYMBOL_CNY = "CNY" 41 | val SYMBOL_INR = "र" 42 | val SYMBOL_BRL = "R$" 43 | val SYMBOL_ZAR = "R" 44 | 45 | fun getSymbol(code: String): String { 46 | when (code) { 47 | CODE_RUB -> return SYMBOL_RUB 48 | CODE_USD -> return SYMBOL_USD 49 | CODE_EUR -> return SYMBOL_EUR 50 | CODE_GBP -> return SYMBOL_GBP 51 | CODE_KZT -> return SYMBOL_KZT 52 | CODE_BYN -> return SYMBOL_BYN 53 | CODE_UAH -> return SYMBOL_UAH 54 | CODE_CHF -> return SYMBOL_CHF 55 | CODE_AZN -> return SYMBOL_AZN 56 | CODE_CZK -> return SYMBOL_CZK 57 | CODE_CAD -> return SYMBOL_CAD 58 | CODE_PLN -> return SYMBOL_PLN 59 | CODE_SEK -> return SYMBOL_SEK 60 | CODE_TRY -> return SYMBOL_TRY 61 | CODE_CNY -> return SYMBOL_CNY 62 | CODE_INR -> return SYMBOL_INR 63 | CODE_BRL -> return SYMBOL_BRL 64 | CODE_ZAR -> return SYMBOL_ZAR 65 | } 66 | return code 67 | } 68 | } 69 | } -------------------------------------------------------------------------------- /sdk/src/main/java/ru/cloudpayments/sdk/models/PayParams.kt: -------------------------------------------------------------------------------- 1 | package ru.cloudpayments.sdk.models 2 | 3 | data class PayParams( 4 | var saveCard: Boolean? = null 5 | ) -------------------------------------------------------------------------------- /sdk/src/main/java/ru/cloudpayments/sdk/models/Transaction.kt: -------------------------------------------------------------------------------- 1 | package ru.cloudpayments.sdk.models 2 | 3 | import ru.cloudpayments.sdk.configuration.CloudpaymentsSDK 4 | 5 | data class Transaction ( 6 | val transactionId: Int?, 7 | val status: CloudpaymentsSDK.TransactionStatus?, 8 | val reasonCode: Int? 9 | ) -------------------------------------------------------------------------------- /sdk/src/main/java/ru/cloudpayments/sdk/scanner/CardData.kt: -------------------------------------------------------------------------------- 1 | package ru.cloudpayments.sdk.scanner 2 | 3 | data class CardData(val cardNumber: String?, // Номер карты без пробелов 4 | val cardExpMonth: String?, // Месяц. Например, январь - 01 5 | val cardExpYear: String?, // Последние 2 цифры года 6 | val cardholderName: String?) -------------------------------------------------------------------------------- /sdk/src/main/java/ru/cloudpayments/sdk/scanner/CardScanner.kt: -------------------------------------------------------------------------------- 1 | package ru.cloudpayments.sdk.scanner 2 | 3 | import android.content.Context 4 | import android.content.Intent 5 | import android.os.Parcelable 6 | import kotlinx.android.parcel.Parcelize 7 | import java.io.Serializable 8 | 9 | abstract class CardScanner: Parcelable { 10 | abstract fun getScannerIntent(context: Context): Intent? 11 | abstract fun getCardDataFromIntent(data: Intent): CardData? 12 | } -------------------------------------------------------------------------------- /sdk/src/main/java/ru/cloudpayments/sdk/ui/dialogs/ThreeDsDialogFragment.kt: -------------------------------------------------------------------------------- 1 | package ru.cloudpayments.sdk.ui.dialogs 2 | 3 | import android.app.Activity 4 | import android.content.Context 5 | import android.os.Bundle 6 | import android.view.LayoutInflater 7 | import android.view.View 8 | import android.view.ViewGroup 9 | import android.webkit.JavascriptInterface 10 | import android.webkit.WebView 11 | import android.webkit.WebViewClient 12 | import androidx.core.view.isGone 13 | import androidx.fragment.app.DialogFragment 14 | import com.google.gson.JsonParser 15 | import org.jsoup.Jsoup 16 | import org.jsoup.nodes.Document 17 | import org.jsoup.nodes.Element 18 | import ru.cloudpayments.sdk.databinding.DialogCpsdkThreeDsBinding 19 | import java.io.UnsupportedEncodingException 20 | import java.net.URLEncoder 21 | import java.util.* 22 | 23 | class ThreeDsDialogFragment : DialogFragment() { 24 | interface ThreeDSDialogListener { 25 | fun onAuthorizationCompleted(md: String, paRes: String) 26 | fun onAuthorizationFailed(error: String?) 27 | } 28 | 29 | companion object { 30 | private const val POST_BACK_URL = "https://demo.cloudpayments.ru/WebFormPost/GetWebViewData" 31 | private const val ARG_ACS_URL = "acs_url" 32 | private const val ARG_MD = "md" 33 | private const val ARG_PA_REQ = "pa_req" 34 | 35 | fun newInstance(acsUrl: String, paReq: String, md: String) = ThreeDsDialogFragment().apply { 36 | arguments = Bundle().also { 37 | it.putString(ARG_ACS_URL, acsUrl) 38 | it.putString(ARG_MD, md) 39 | it.putString(ARG_PA_REQ, paReq) 40 | } 41 | } 42 | } 43 | 44 | private var _binding: DialogCpsdkThreeDsBinding? = null 45 | 46 | private val binding get() = _binding!! 47 | 48 | override fun onCreateView( 49 | inflater: LayoutInflater, 50 | container: ViewGroup?, 51 | savedInstanceState: Bundle? 52 | ): View? { 53 | _binding = DialogCpsdkThreeDsBinding.inflate(inflater, container, false) 54 | return binding.root 55 | } 56 | 57 | override fun onDestroyView() { 58 | super.onDestroyView() 59 | _binding = null 60 | } 61 | 62 | private val acsUrl by lazy { 63 | requireArguments().getString(ARG_ACS_URL) ?: "" 64 | } 65 | 66 | private val md by lazy { 67 | requireArguments().getString(ARG_MD) ?: "" 68 | } 69 | 70 | private val paReq by lazy { 71 | requireArguments().getString(ARG_PA_REQ) ?: "" 72 | } 73 | 74 | private var listener: ThreeDSDialogListener? = null 75 | 76 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 77 | super.onViewCreated(view, savedInstanceState) 78 | 79 | isCancelable = false 80 | 81 | binding.webView.webViewClient = ThreeDsWebViewClient() 82 | binding.webView.settings.domStorageEnabled = true 83 | binding.webView.settings.javaScriptEnabled = true 84 | binding.webView.settings.javaScriptCanOpenWindowsAutomatically = true 85 | binding.webView.addJavascriptInterface(ThreeDsJavaScriptInterface(), "JavaScriptThreeDs") 86 | 87 | try { 88 | val params = StringBuilder() 89 | .append("PaReq=").append(URLEncoder.encode(paReq, "UTF-8")) 90 | .append("&MD=").append(URLEncoder.encode(md, "UTF-8")) 91 | .append("&TermUrl=").append(URLEncoder.encode(POST_BACK_URL, "UTF-8")) 92 | .toString() 93 | binding.webView.postUrl(acsUrl, params.toByteArray()) 94 | } catch (e: UnsupportedEncodingException) { 95 | e.printStackTrace() 96 | } 97 | 98 | binding.icClose.setOnClickListener { 99 | listener?.onAuthorizationFailed(null) 100 | dismiss() 101 | } 102 | } 103 | 104 | override fun onStart() { 105 | super.onStart() 106 | val window = dialog!!.window 107 | window?.setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT) 108 | } 109 | 110 | private inner class ThreeDsWebViewClient : WebViewClient() { 111 | override fun onPageFinished(view: WebView, url: String) { 112 | if (url.toLowerCase(Locale.getDefault()) == POST_BACK_URL.toLowerCase(Locale.getDefault())) { 113 | view.isGone = true 114 | view.loadUrl("javascript:window.JavaScriptThreeDs.processHTML(''+document.getElementsByTagName('html')[0].innerHTML+'');") 115 | } 116 | } 117 | } 118 | 119 | internal inner class ThreeDsJavaScriptInterface { 120 | @JavascriptInterface 121 | fun processHTML(html: String?) { 122 | val doc: Document = Jsoup.parse(html) 123 | val element: Element? = doc.select("body").first() 124 | val jsonObject = JsonParser().parse(element?.ownText()).asJsonObject 125 | val paRes = jsonObject["PaRes"].asString 126 | requireActivity().runOnUiThread { 127 | if (!paRes.isNullOrEmpty()) { 128 | listener?.onAuthorizationCompleted(md, paRes) 129 | } else { 130 | listener?.onAuthorizationFailed(html ?: "") 131 | } 132 | dismissAllowingStateLoss() 133 | } 134 | } 135 | } 136 | 137 | override fun onAttach(context: Context) { 138 | super.onAttach(context) 139 | 140 | listener = targetFragment as? ThreeDSDialogListener 141 | if (listener == null) { 142 | listener = context as? ThreeDSDialogListener 143 | } 144 | } 145 | 146 | override fun onAttach(activity: Activity) { 147 | super.onAttach(activity) 148 | 149 | listener = targetFragment as? ThreeDSDialogListener 150 | if (listener == null) { 151 | listener = activity as? ThreeDSDialogListener 152 | } 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /sdk/src/main/java/ru/cloudpayments/sdk/ui/dialogs/base/BasePaymentBottomSheetFragment.kt: -------------------------------------------------------------------------------- 1 | package ru.cloudpayments.sdk.ui.dialogs.base 2 | 3 | import android.content.res.ColorStateList 4 | import android.os.Bundle 5 | import android.view.View 6 | import android.view.animation.Animation 7 | import android.view.animation.AnimationUtils 8 | import androidx.core.content.ContextCompat 9 | import com.google.android.material.textfield.TextInputEditText 10 | import com.google.android.material.textfield.TextInputLayout 11 | import ru.cloudpayments.sdk.R 12 | import ru.cloudpayments.sdk.configuration.PaymentConfiguration 13 | import ru.cloudpayments.sdk.ui.PaymentActivity 14 | import ru.cloudpayments.sdk.viewmodel.BaseViewModel 15 | import ru.cloudpayments.sdk.viewmodel.BaseViewState 16 | 17 | 18 | internal abstract class BasePaymentBottomSheetFragment>: BaseVMBottomSheetFragment() { 19 | interface IPaymentFragment { 20 | fun paymentWillFinish() 21 | } 22 | 23 | private fun getConfiguration(): PaymentConfiguration? { 24 | if (activity is PaymentActivity) { 25 | return (activity as PaymentActivity).paymentConfiguration 26 | } 27 | return null 28 | } 29 | 30 | protected val paymentConfiguration by lazy { 31 | getConfiguration() 32 | } 33 | 34 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 35 | super.onViewCreated(view, savedInstanceState) 36 | 37 | val fadeAnim = AnimationUtils.loadAnimation(requireContext(), R.anim.cpsdk_fade_in) 38 | fadeAnim.fillAfter = true 39 | 40 | val slideAnim = AnimationUtils.loadAnimation(requireContext(), R.anim.cpsdk_slide_in) 41 | slideAnim.fillAfter = true 42 | } 43 | 44 | protected fun close(force: Boolean, completion: (() -> (Unit))? = null){ 45 | val slideAnim = AnimationUtils.loadAnimation(requireContext(), R.anim.cpsdk_slide_out) 46 | slideAnim.fillAfter = true 47 | slideAnim.setAnimationListener(object : Animation.AnimationListener{ 48 | override fun onAnimationStart(animation: Animation?) { 49 | } 50 | 51 | override fun onAnimationEnd(animation: Animation?) { 52 | animation?.setAnimationListener(null) 53 | requireActivity().supportFragmentManager.popBackStack() 54 | if (force) { 55 | val listener = requireActivity() as? IPaymentFragment 56 | listener?.paymentWillFinish() 57 | } 58 | completion?.invoke() 59 | } 60 | 61 | override fun onAnimationRepeat(animation: Animation?) { 62 | } 63 | }) 64 | 65 | val fadeAnim = AnimationUtils.loadAnimation(requireContext(), R.anim.cpsdk_fade_out) 66 | fadeAnim.fillAfter = true 67 | } 68 | 69 | fun handleBackButton(){ 70 | close(true) 71 | } 72 | 73 | internal fun activity(): PaymentActivity { 74 | return activity as PaymentActivity 75 | } 76 | 77 | protected fun errorMode(isErrorMode: Boolean, editText: TextInputEditText, editLayout: TextInputLayout){ 78 | if (isErrorMode) { 79 | 80 | val csl = ColorStateList( 81 | arrayOf(intArrayOf(android.R.attr.state_pressed), intArrayOf()), 82 | intArrayOf( 83 | ContextCompat.getColor(requireContext(), R.color.cpsdk_pale_red), 84 | ContextCompat.getColor(requireContext(), R.color.cpsdk_pale_red) 85 | ) 86 | ) 87 | 88 | editLayout.defaultHintTextColor = csl 89 | editLayout.hintTextColor = csl 90 | editText.setTextColor(ContextCompat.getColor(requireContext(), R.color.cpsdk_pale_red)) 91 | editText.setBackgroundResource(R.drawable.cpsdk_bg_edit_text_selector_error) 92 | } else { 93 | 94 | val csl = ColorStateList( 95 | arrayOf(intArrayOf(android.R.attr.state_pressed), intArrayOf()), 96 | intArrayOf( 97 | ContextCompat.getColor(requireContext(), R.color.cpsdk_edit_text_hint), 98 | ContextCompat.getColor(requireContext(), R.color.cpsdk_edit_text_hint) 99 | ) 100 | ) 101 | 102 | editLayout.defaultHintTextColor = csl 103 | editLayout.hintTextColor = csl 104 | editText.setTextColor(ContextCompat.getColor(requireContext(), R.color.cpsdk_dark)) 105 | editText.setBackgroundResource(R.drawable.cpsdk_bg_edit_text_selector) 106 | } 107 | } 108 | } -------------------------------------------------------------------------------- /sdk/src/main/java/ru/cloudpayments/sdk/ui/dialogs/base/BasePaymentDialogFragment.kt: -------------------------------------------------------------------------------- 1 | package ru.cloudpayments.sdk.ui.dialogs.base 2 | 3 | import android.os.Bundle 4 | import android.view.View 5 | import android.view.animation.Animation 6 | import android.view.animation.AnimationUtils 7 | import androidx.core.content.ContextCompat 8 | import com.google.android.material.textfield.TextInputEditText 9 | import ru.cloudpayments.sdk.R 10 | import ru.cloudpayments.sdk.configuration.PaymentConfiguration 11 | import ru.cloudpayments.sdk.ui.PaymentActivity 12 | import ru.cloudpayments.sdk.viewmodel.BaseViewModel 13 | import ru.cloudpayments.sdk.viewmodel.BaseViewState 14 | 15 | internal abstract class BasePaymentDialogFragment>: BaseVMDialogFragment() { 16 | interface IPaymentFragment { 17 | fun paymentWillFinish() 18 | } 19 | 20 | private fun getConfiguration(): PaymentConfiguration? { 21 | if (activity is PaymentActivity) { 22 | return (activity as PaymentActivity).paymentConfiguration 23 | } 24 | return null 25 | } 26 | 27 | protected val paymentConfiguration by lazy { 28 | getConfiguration() 29 | } 30 | 31 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 32 | super.onViewCreated(view, savedInstanceState) 33 | 34 | val fadeAnim = AnimationUtils.loadAnimation(requireContext(), R.anim.cpsdk_fade_in) 35 | fadeAnim.fillAfter = true 36 | 37 | val slideAnim = AnimationUtils.loadAnimation(requireContext(), R.anim.cpsdk_slide_in) 38 | slideAnim.fillAfter = true 39 | } 40 | 41 | protected fun close(force: Boolean, completion: (() -> (Unit))? = null){ 42 | val slideAnim = AnimationUtils.loadAnimation(requireContext(), R.anim.cpsdk_slide_out) 43 | slideAnim.fillAfter = true 44 | slideAnim.setAnimationListener(object : Animation.AnimationListener{ 45 | override fun onAnimationStart(animation: Animation?) { 46 | } 47 | 48 | override fun onAnimationEnd(animation: Animation?) { 49 | animation?.setAnimationListener(null) 50 | requireActivity().supportFragmentManager.popBackStack() 51 | if (force) { 52 | val listener = requireActivity() as? IPaymentFragment 53 | listener?.paymentWillFinish() 54 | } 55 | completion?.invoke() 56 | } 57 | 58 | override fun onAnimationRepeat(animation: Animation?) { 59 | } 60 | }) 61 | 62 | val fadeAnim = AnimationUtils.loadAnimation(requireContext(), R.anim.cpsdk_fade_out) 63 | fadeAnim.fillAfter = true 64 | } 65 | 66 | fun handleBackButton(){ 67 | close(true) 68 | } 69 | 70 | internal fun activity(): PaymentActivity { 71 | return activity as PaymentActivity 72 | } 73 | 74 | protected fun errorMode(isErrorMode: Boolean, editText: TextInputEditText){ 75 | if (isErrorMode) { 76 | editText.setTextColor(ContextCompat.getColor(requireContext(), R.color.cpsdk_pale_red)) 77 | editText.setBackgroundResource(R.drawable.cpsdk_bg_edit_text_selector_error) 78 | } else { 79 | editText.setTextColor(ContextCompat.getColor(requireContext(), R.color.cpsdk_dark)) 80 | editText.setBackgroundResource(R.drawable.cpsdk_bg_edit_text_selector) 81 | } 82 | } 83 | } -------------------------------------------------------------------------------- /sdk/src/main/java/ru/cloudpayments/sdk/ui/dialogs/base/BaseVMBottomSheetFragment.kt: -------------------------------------------------------------------------------- 1 | package ru.cloudpayments.sdk.ui.dialogs.base 2 | 3 | import android.content.DialogInterface 4 | import android.os.Bundle 5 | import android.view.View 6 | import androidx.lifecycle.Observer 7 | import com.google.android.material.bottomsheet.BottomSheetDialogFragment 8 | import ru.cloudpayments.sdk.viewmodel.BaseViewModel 9 | import ru.cloudpayments.sdk.viewmodel.BaseViewState 10 | 11 | internal abstract class BaseVMBottomSheetFragment>: BottomSheetDialogFragment() { 12 | abstract val viewModel: VM 13 | abstract fun render(state: VS) 14 | 15 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 16 | super.onViewCreated(view, savedInstanceState) 17 | 18 | viewModel.viewState.observe(viewLifecycleOwner, Observer { 19 | render(it) 20 | }) 21 | 22 | } 23 | override fun onCancel(dialog: DialogInterface) { 24 | super.onCancel(dialog) 25 | activity?.finish() 26 | } 27 | } -------------------------------------------------------------------------------- /sdk/src/main/java/ru/cloudpayments/sdk/ui/dialogs/base/BaseVMDialogFragment.kt: -------------------------------------------------------------------------------- 1 | package ru.cloudpayments.sdk.ui.dialogs.base 2 | 3 | import android.content.DialogInterface 4 | import android.os.Bundle 5 | import android.view.View 6 | import androidx.constraintlayout.widget.Constraints 7 | import androidx.fragment.app.DialogFragment 8 | import androidx.lifecycle.Observer 9 | import ru.cloudpayments.sdk.R 10 | import ru.cloudpayments.sdk.viewmodel.BaseViewModel 11 | import ru.cloudpayments.sdk.viewmodel.BaseViewState 12 | 13 | 14 | internal abstract class BaseVMDialogFragment> : 15 | DialogFragment() { 16 | abstract val viewModel: VM 17 | abstract fun render(state: VS) 18 | 19 | override fun onCreate(savedInstanceState: Bundle?) { 20 | super.onCreate(savedInstanceState) 21 | setStyle(STYLE_NO_TITLE, R.style.cpsdk_Dialog); 22 | } 23 | 24 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 25 | super.onViewCreated(view, savedInstanceState) 26 | 27 | viewModel.viewState.observe(viewLifecycleOwner, Observer { 28 | render(it) 29 | }) 30 | } 31 | 32 | override fun onStart() { 33 | super.onStart() 34 | 35 | dialog?.window?.setLayout( 36 | Constraints.LayoutParams.MATCH_PARENT, 37 | Constraints.LayoutParams.WRAP_CONTENT 38 | ) // full width dialog 39 | dialog?.setCancelable(false) 40 | 41 | } 42 | 43 | override fun onCancel(dialog: DialogInterface) { 44 | super.onCancel(dialog) 45 | activity?.finish() 46 | } 47 | } -------------------------------------------------------------------------------- /sdk/src/main/java/ru/cloudpayments/sdk/util/Constants.kt: -------------------------------------------------------------------------------- 1 | package ru.cloudpayments.sdk.util 2 | 3 | import com.google.android.gms.wallet.WalletConstants 4 | import ru.cloudpayments.sdk.BuildConfig 5 | 6 | val TAG = "Payment SDK" 7 | 8 | val GOOGLE_PAY_ENVIRONMENT = if (BuildConfig.DEBUG) 9 | WalletConstants.ENVIRONMENT_TEST else 10 | WalletConstants.ENVIRONMENT_PRODUCTION 11 | 12 | val GOOGLE_PAY_SUPPORTED_NETWORKS = arrayListOf( 13 | WalletConstants.CARD_NETWORK_VISA, 14 | WalletConstants.CARD_NETWORK_MASTERCARD, 15 | WalletConstants.CARD_NETWORK_AMEX, 16 | WalletConstants.CARD_NETWORK_DISCOVER, 17 | WalletConstants.CARD_NETWORK_JCB, 18 | WalletConstants.CARD_NETWORK_INTERAC) 19 | 20 | val GOOGLE_PAY_SUPPORTED_METHODS = arrayListOf( 21 | WalletConstants.PAYMENT_METHOD_CARD, 22 | WalletConstants.PAYMENT_METHOD_TOKENIZED_CARD) -------------------------------------------------------------------------------- /sdk/src/main/java/ru/cloudpayments/sdk/util/Extensions.kt: -------------------------------------------------------------------------------- 1 | package ru.cloudpayments.sdk.util 2 | 3 | import android.app.Activity 4 | import android.content.Context 5 | import android.os.Build 6 | import android.view.WindowManager 7 | import android.view.inputmethod.InputMethodManager 8 | import androidx.fragment.app.Fragment 9 | import androidx.fragment.app.FragmentActivity 10 | import ru.cloudpayments.sdk.R 11 | import java.text.NumberFormat 12 | 13 | fun Activity.hideKeyboard() { 14 | currentFocus?.let { 15 | window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN) 16 | val inputMethodManager = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager 17 | inputMethodManager.hideSoftInputFromWindow(it.windowToken, InputMethodManager.HIDE_NOT_ALWAYS) 18 | } 19 | } 20 | 21 | fun Activity.showKeyboard(){ 22 | currentFocus?.let { 23 | val imm = getSystemService(Activity.INPUT_METHOD_SERVICE) as InputMethodManager 24 | imm.showSoftInput(currentFocus, 0) 25 | } 26 | } 27 | 28 | fun Fragment.nextFragment(fragment: Fragment, addToBackStack: Boolean = true, contentFrame: Int, animated: Boolean = false){ 29 | activity?.nextFragment(fragment, addToBackStack, contentFrame, animated) 30 | } 31 | 32 | fun FragmentActivity.nextFragment(fragment: Fragment, addToBackStack: Boolean = true, contentFrame: Int, animated: Boolean = false) { 33 | hideKeyboard() 34 | 35 | val transaction = supportFragmentManager.beginTransaction() 36 | 37 | if (animated) { 38 | transaction.setCustomAnimations(R.anim.cpsdk_slide_in, R.anim.cpsdk_slide_out) 39 | } 40 | 41 | transaction.add(contentFrame, fragment, supportFragmentManager.backStackEntryCount.toString()) 42 | 43 | if (addToBackStack) { 44 | transaction.addToBackStack(fragment::class.java.toString()) 45 | } 46 | transaction.commit() 47 | } 48 | 49 | fun Context.getCurrencyString(currency: Double) = if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { 50 | NumberFormat.getCurrencyInstance(getRussianLocale()).format(currency) 51 | } else { 52 | getString(R.string.cpsdk_currency_template, currency) 53 | } -------------------------------------------------------------------------------- /sdk/src/main/java/ru/cloudpayments/sdk/util/GooglePayHandler.kt: -------------------------------------------------------------------------------- 1 | package ru.cloudpayments.sdk.util 2 | 3 | import com.google.android.gms.wallet.* 4 | import io.ashdavies.rx.rxtasks.toSingle 5 | import io.reactivex.Single 6 | import ru.cloudpayments.sdk.configuration.PaymentConfiguration 7 | import ru.cloudpayments.sdk.ui.PaymentActivity 8 | 9 | internal class GooglePayHandler { 10 | companion object { 11 | fun present(configuration: PaymentConfiguration, activity: PaymentActivity, requestCode: Int) { 12 | val transactionInfo = TransactionInfo.newBuilder() 13 | .setTotalPriceStatus(WalletConstants.TOTAL_PRICE_STATUS_FINAL) 14 | .setTotalPrice(configuration.paymentData.amount) 15 | .setCurrencyCode(configuration.paymentData.currency) 16 | .build() 17 | val request = createPaymentDataRequest(transactionInfo, configuration.publicId) 18 | val client = createPaymentsClient(activity) 19 | AutoResolveHelper.resolveTask(client.loadPaymentData(request), activity, requestCode) 20 | } 21 | 22 | private fun createPaymentDataRequest(transactionInfo: TransactionInfo, publicId: String): PaymentDataRequest { 23 | val paramsBuilder = PaymentMethodTokenizationParameters.newBuilder() 24 | .setPaymentMethodTokenizationType( 25 | WalletConstants.PAYMENT_METHOD_TOKENIZATION_TYPE_PAYMENT_GATEWAY) 26 | .addParameter("gateway", "cloudpayments") 27 | .addParameter("gatewayMerchantId", publicId) 28 | 29 | return createPaymentDataRequest(transactionInfo, paramsBuilder.build()) 30 | } 31 | 32 | private fun createPaymentDataRequest(transactionInfo: TransactionInfo, params: PaymentMethodTokenizationParameters): PaymentDataRequest { 33 | return PaymentDataRequest.newBuilder() 34 | .setPhoneNumberRequired(false) 35 | .setEmailRequired(false) 36 | .setShippingAddressRequired(false) 37 | .setTransactionInfo(transactionInfo) 38 | .addAllowedPaymentMethods(GOOGLE_PAY_SUPPORTED_METHODS) 39 | .setCardRequirements( 40 | CardRequirements.newBuilder() 41 | .addAllowedCardNetworks(GOOGLE_PAY_SUPPORTED_NETWORKS) 42 | .setAllowPrepaidCards(true) 43 | .setBillingAddressRequired(false) 44 | .setBillingAddressFormat(WalletConstants.BILLING_ADDRESS_FORMAT_MIN) 45 | .build()) 46 | .setPaymentMethodTokenizationParameters(params) 47 | .setUiRequired(true) 48 | .build() 49 | } 50 | 51 | private fun createPaymentsClient(activity: PaymentActivity): PaymentsClient { 52 | val walletOptions = Wallet.WalletOptions.Builder() 53 | .setEnvironment(GOOGLE_PAY_ENVIRONMENT) 54 | .build() 55 | return Wallet.getPaymentsClient(activity, walletOptions) 56 | } 57 | 58 | fun isReadyToMakeGooglePay(activity: PaymentActivity): Single { 59 | val request = IsReadyToPayRequest.newBuilder() 60 | for (allowedMethod in GOOGLE_PAY_SUPPORTED_METHODS) { 61 | request.addAllowedPaymentMethod(allowedMethod) 62 | } 63 | return createPaymentsClient(activity).isReadyToPay(request.build()).toSingle() 64 | } 65 | } 66 | } -------------------------------------------------------------------------------- /sdk/src/main/java/ru/cloudpayments/sdk/util/HexPacketHelper.kt: -------------------------------------------------------------------------------- 1 | package ru.cloudpayments.sdk.util 2 | 3 | class HexPacketHelper { 4 | 5 | companion object { 6 | fun numberToEvenLengthString(number: Int): String { 7 | var numberStr = number.toString() 8 | 9 | return if (numberStr.length % 2 == 0) numberStr 10 | else "0$numberStr"; 11 | } 12 | } 13 | } -------------------------------------------------------------------------------- /sdk/src/main/java/ru/cloudpayments/sdk/util/InjectorUtils.kt: -------------------------------------------------------------------------------- 1 | package ru.cloudpayments.sdk.util 2 | 3 | import ru.cloudpayments.sdk.configuration.PaymentData 4 | import ru.cloudpayments.sdk.viewmodel.PaymentProcessViewModelFactory 5 | 6 | internal object InjectorUtils { 7 | fun providePaymentProcessViewModelFactory(paymentData: PaymentData, cryptogram: String, useDualMessagePayment: Boolean, saveCard: Boolean?): PaymentProcessViewModelFactory { 8 | return PaymentProcessViewModelFactory(paymentData, cryptogram, useDualMessagePayment, saveCard) 9 | } 10 | } -------------------------------------------------------------------------------- /sdk/src/main/java/ru/cloudpayments/sdk/util/PublicKey.kt: -------------------------------------------------------------------------------- 1 | package ru.cloudpayments.sdk.util 2 | 3 | import android.app.Activity 4 | import android.content.Context 5 | import android.content.SharedPreferences 6 | 7 | class PublicKey private constructor() { 8 | 9 | companion object { 10 | private val publicKey = PublicKey() 11 | private lateinit var sharedPreferences: SharedPreferences 12 | 13 | private const val PEM = "pem" 14 | private const val VERSION = "version" 15 | 16 | fun getInstance(context: Context): PublicKey { 17 | if (!::sharedPreferences.isInitialized) { 18 | synchronized(PublicKey::class.java) { 19 | if (!::sharedPreferences.isInitialized) { 20 | sharedPreferences = context.getSharedPreferences(context.packageName, Activity.MODE_PRIVATE) 21 | } 22 | } 23 | } 24 | return publicKey 25 | } 26 | } 27 | 28 | val pem: String? 29 | get() = sharedPreferences.getString(PEM, "") 30 | 31 | fun savePem(pem: String) { 32 | sharedPreferences.edit() 33 | .putString(PEM, pem) 34 | .apply() 35 | } 36 | 37 | val version: Int? 38 | get() = sharedPreferences.getInt(VERSION, 0) 39 | 40 | fun saveVersion(version: Int) { 41 | sharedPreferences.edit() 42 | .putInt(VERSION, version) 43 | .apply() 44 | } 45 | 46 | fun clearAll() { 47 | sharedPreferences.edit().clear().apply() 48 | } 49 | 50 | } -------------------------------------------------------------------------------- /sdk/src/main/java/ru/cloudpayments/sdk/util/Tools.kt: -------------------------------------------------------------------------------- 1 | package ru.cloudpayments.sdk.util 2 | 3 | import android.text.Editable 4 | import android.text.TextWatcher 5 | import java.util.* 6 | import java.util.regex.Pattern 7 | 8 | open class TextWatcherAdapter: TextWatcher { 9 | override fun afterTextChanged(s: Editable?) {} 10 | 11 | override fun beforeTextChanged(s: CharSequence?, p1: Int, p2: Int, p3: Int) {} 12 | 13 | override fun onTextChanged(s: CharSequence?, p1: Int, p2: Int, p3: Int) {} 14 | } 15 | 16 | fun emailIsValid(email: String?): Boolean { 17 | if (email.isNullOrBlank()) { 18 | return false 19 | } 20 | 21 | val emailPattern = "^.+@([A-Za-z0-9-]+\\.)+[A-Za-z]{2}[A-Za-z]*\$" 22 | return Pattern.compile(emailPattern).matcher(email).matches() 23 | } 24 | 25 | fun getRussianLocale() = Locale("ru", "RU") -------------------------------------------------------------------------------- /sdk/src/main/java/ru/cloudpayments/sdk/viewmodel/BaseViewModel.kt: -------------------------------------------------------------------------------- 1 | package ru.cloudpayments.sdk.viewmodel 2 | 3 | import androidx.lifecycle.MutableLiveData 4 | import androidx.lifecycle.ViewModel 5 | 6 | internal abstract class BaseViewModel : ViewModel() { 7 | abstract val viewState: MutableLiveData 8 | abstract var currentState: VS 9 | } -------------------------------------------------------------------------------- /sdk/src/main/java/ru/cloudpayments/sdk/viewmodel/BaseViewState.kt: -------------------------------------------------------------------------------- 1 | package ru.cloudpayments.sdk.viewmodel 2 | 3 | internal abstract class BaseViewState -------------------------------------------------------------------------------- /sdk/src/main/java/ru/cloudpayments/sdk/viewmodel/PaymentCardViewModel.kt: -------------------------------------------------------------------------------- 1 | package ru.cloudpayments.sdk.viewmodel 2 | 3 | import androidx.lifecycle.MutableLiveData 4 | import io.reactivex.disposables.Disposable 5 | import ru.cloudpayments.sdk.api.CloudpaymentsApi 6 | import javax.inject.Inject 7 | 8 | internal class PaymentCardViewModel: BaseViewModel() { 9 | override var currentState = PaymentCardViewState() 10 | override val viewState: MutableLiveData by lazy { 11 | MutableLiveData(currentState) 12 | } 13 | 14 | private var disposable: Disposable? = null 15 | 16 | @Inject lateinit var api: CloudpaymentsApi 17 | 18 | override fun onCleared() { 19 | super.onCleared() 20 | 21 | disposable?.dispose() 22 | } 23 | } 24 | 25 | internal data class PaymentCardViewState(val a: String? = null): BaseViewState() -------------------------------------------------------------------------------- /sdk/src/main/java/ru/cloudpayments/sdk/viewmodel/PaymentOptionsViewModel.kt: -------------------------------------------------------------------------------- 1 | package ru.cloudpayments.sdk.viewmodel 2 | 3 | import androidx.lifecycle.MutableLiveData 4 | import io.reactivex.android.schedulers.AndroidSchedulers 5 | import io.reactivex.disposables.Disposable 6 | import ru.cloudpayments.sdk.api.CloudpaymentsApi 7 | import javax.inject.Inject 8 | 9 | internal class PaymentOptionsViewModel: BaseViewModel() { 10 | override var currentState = PaymentOptionsViewState() 11 | override val viewState: MutableLiveData by lazy { 12 | MutableLiveData(currentState) 13 | } 14 | 15 | private var disposable: Disposable? = null 16 | 17 | @Inject 18 | lateinit var api: CloudpaymentsApi 19 | 20 | fun getPublicKey() { 21 | disposable = api.getPublicKey() 22 | .toObservable() 23 | .observeOn(AndroidSchedulers.mainThread()) 24 | .map { response -> 25 | val state = currentState.copy(publicKeyPem = response.pem, publicKeyVersion = response.version) 26 | stateChanged(state) 27 | } 28 | .onErrorReturn { 29 | 30 | } 31 | .subscribe() 32 | } 33 | 34 | fun getMerchantConfiguration(publicId: String) { 35 | disposable = api.getMerchantConfiguration(publicId) 36 | .toObservable() 37 | .observeOn(AndroidSchedulers.mainThread()) 38 | .map { response -> 39 | 40 | var isTinkoffPayAvailable = false 41 | 42 | for (paymentMethod in response.model?.externalPaymentMethods!!) { 43 | if (paymentMethod.type == 6) { 44 | isTinkoffPayAvailable = paymentMethod.enabled!! 45 | break 46 | } 47 | } 48 | 49 | val state = currentState.copy(isTinkoffPayAvailable = isTinkoffPayAvailable, isSaveCard = response.model?.features?.isSaveCard) 50 | stateChanged(state) 51 | } 52 | .onErrorReturn { 53 | 54 | } 55 | .subscribe() 56 | } 57 | 58 | private fun stateChanged(viewState: PaymentOptionsViewState) { 59 | currentState = viewState.copy() 60 | this.viewState.apply { 61 | value = viewState 62 | } 63 | } 64 | 65 | override fun onCleared() { 66 | super.onCleared() 67 | 68 | disposable?.dispose() 69 | } 70 | } 71 | 72 | internal data class PaymentOptionsViewState( 73 | val publicKeyPem: String? = null, 74 | val publicKeyVersion: Int? = null, 75 | val isTinkoffPayAvailable: Boolean? = null, 76 | val isSaveCard: Int? = null 77 | ): BaseViewState() -------------------------------------------------------------------------------- /sdk/src/main/java/ru/cloudpayments/sdk/viewmodel/PaymentProcessViewModelFactory.kt: -------------------------------------------------------------------------------- 1 | package ru.cloudpayments.sdk.viewmodel 2 | 3 | import androidx.lifecycle.ViewModel 4 | import androidx.lifecycle.ViewModelProvider 5 | import ru.cloudpayments.sdk.configuration.PaymentData 6 | 7 | internal class PaymentProcessViewModelFactory( 8 | private val paymentData: PaymentData, 9 | private val cryptogram: String, 10 | private val useDualMessagePayment: Boolean, 11 | private val saveCard: Boolean? 12 | ): ViewModelProvider.Factory { 13 | 14 | @Suppress("UNCHECKED_CAST") 15 | override fun create(modelClass: Class): T { 16 | return PaymentProcessViewModel(paymentData, cryptogram, useDualMessagePayment, saveCard) as T 17 | } 18 | } -------------------------------------------------------------------------------- /sdk/src/main/res/anim/cpsdk_fade_in.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 8 | -------------------------------------------------------------------------------- /sdk/src/main/res/anim/cpsdk_fade_out.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 8 | -------------------------------------------------------------------------------- /sdk/src/main/res/anim/cpsdk_slide_in.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 8 | -------------------------------------------------------------------------------- /sdk/src/main/res/anim/cpsdk_slide_out.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 8 | -------------------------------------------------------------------------------- /sdk/src/main/res/color/cpsdk_color_edittext.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /sdk/src/main/res/drawable-hdpi/cpsdk_button_tinkoff_pay.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudpayments/CloudPayments-SDK-Android/2f022fe6f242486cc000a1a16ffa03df2def5049/sdk/src/main/res/drawable-hdpi/cpsdk_button_tinkoff_pay.png -------------------------------------------------------------------------------- /sdk/src/main/res/drawable-hdpi/cpsdk_ic_checkbox_checked.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudpayments/CloudPayments-SDK-Android/2f022fe6f242486cc000a1a16ffa03df2def5049/sdk/src/main/res/drawable-hdpi/cpsdk_ic_checkbox_checked.png -------------------------------------------------------------------------------- /sdk/src/main/res/drawable-hdpi/cpsdk_ic_checkbox_unchecked.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudpayments/CloudPayments-SDK-Android/2f022fe6f242486cc000a1a16ffa03df2def5049/sdk/src/main/res/drawable-hdpi/cpsdk_ic_checkbox_unchecked.png -------------------------------------------------------------------------------- /sdk/src/main/res/drawable-hdpi/cpsdk_ic_failure.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudpayments/CloudPayments-SDK-Android/2f022fe6f242486cc000a1a16ffa03df2def5049/sdk/src/main/res/drawable-hdpi/cpsdk_ic_failure.png -------------------------------------------------------------------------------- /sdk/src/main/res/drawable-hdpi/cpsdk_ic_progress.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudpayments/CloudPayments-SDK-Android/2f022fe6f242486cc000a1a16ffa03df2def5049/sdk/src/main/res/drawable-hdpi/cpsdk_ic_progress.png -------------------------------------------------------------------------------- /sdk/src/main/res/drawable-hdpi/cpsdk_ic_ps_jcb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudpayments/CloudPayments-SDK-Android/2f022fe6f242486cc000a1a16ffa03df2def5049/sdk/src/main/res/drawable-hdpi/cpsdk_ic_ps_jcb.png -------------------------------------------------------------------------------- /sdk/src/main/res/drawable-hdpi/cpsdk_ic_ps_mir.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudpayments/CloudPayments-SDK-Android/2f022fe6f242486cc000a1a16ffa03df2def5049/sdk/src/main/res/drawable-hdpi/cpsdk_ic_ps_mir.png -------------------------------------------------------------------------------- /sdk/src/main/res/drawable-hdpi/cpsdk_ic_success.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudpayments/CloudPayments-SDK-Android/2f022fe6f242486cc000a1a16ffa03df2def5049/sdk/src/main/res/drawable-hdpi/cpsdk_ic_success.png -------------------------------------------------------------------------------- /sdk/src/main/res/drawable-hdpi/drag_handle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudpayments/CloudPayments-SDK-Android/2f022fe6f242486cc000a1a16ffa03df2def5049/sdk/src/main/res/drawable-hdpi/drag_handle.png -------------------------------------------------------------------------------- /sdk/src/main/res/drawable-hdpi/ic_required_email.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudpayments/CloudPayments-SDK-Android/2f022fe6f242486cc000a1a16ffa03df2def5049/sdk/src/main/res/drawable-hdpi/ic_required_email.png -------------------------------------------------------------------------------- /sdk/src/main/res/drawable-mdpi/cpsdk_button_tinkoff_pay.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudpayments/CloudPayments-SDK-Android/2f022fe6f242486cc000a1a16ffa03df2def5049/sdk/src/main/res/drawable-mdpi/cpsdk_button_tinkoff_pay.png -------------------------------------------------------------------------------- /sdk/src/main/res/drawable-mdpi/cpsdk_ic_checkbox_unchecked.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudpayments/CloudPayments-SDK-Android/2f022fe6f242486cc000a1a16ffa03df2def5049/sdk/src/main/res/drawable-mdpi/cpsdk_ic_checkbox_unchecked.png -------------------------------------------------------------------------------- /sdk/src/main/res/drawable-mdpi/cpsdk_ic_failure.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudpayments/CloudPayments-SDK-Android/2f022fe6f242486cc000a1a16ffa03df2def5049/sdk/src/main/res/drawable-mdpi/cpsdk_ic_failure.png -------------------------------------------------------------------------------- /sdk/src/main/res/drawable-mdpi/cpsdk_ic_progress.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudpayments/CloudPayments-SDK-Android/2f022fe6f242486cc000a1a16ffa03df2def5049/sdk/src/main/res/drawable-mdpi/cpsdk_ic_progress.png -------------------------------------------------------------------------------- /sdk/src/main/res/drawable-mdpi/cpsdk_ic_ps_jcb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudpayments/CloudPayments-SDK-Android/2f022fe6f242486cc000a1a16ffa03df2def5049/sdk/src/main/res/drawable-mdpi/cpsdk_ic_ps_jcb.png -------------------------------------------------------------------------------- /sdk/src/main/res/drawable-mdpi/cpsdk_ic_ps_mir.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudpayments/CloudPayments-SDK-Android/2f022fe6f242486cc000a1a16ffa03df2def5049/sdk/src/main/res/drawable-mdpi/cpsdk_ic_ps_mir.png -------------------------------------------------------------------------------- /sdk/src/main/res/drawable-mdpi/cpsdk_ic_success.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudpayments/CloudPayments-SDK-Android/2f022fe6f242486cc000a1a16ffa03df2def5049/sdk/src/main/res/drawable-mdpi/cpsdk_ic_success.png -------------------------------------------------------------------------------- /sdk/src/main/res/drawable-mdpi/drag_handle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudpayments/CloudPayments-SDK-Android/2f022fe6f242486cc000a1a16ffa03df2def5049/sdk/src/main/res/drawable-mdpi/drag_handle.png -------------------------------------------------------------------------------- /sdk/src/main/res/drawable-mdpi/ic_required_email.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudpayments/CloudPayments-SDK-Android/2f022fe6f242486cc000a1a16ffa03df2def5049/sdk/src/main/res/drawable-mdpi/ic_required_email.png -------------------------------------------------------------------------------- /sdk/src/main/res/drawable-v21/cpsdk_bg_button_gp.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /sdk/src/main/res/drawable-xhdpi/cpsdk_button_tinkoff_pay.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudpayments/CloudPayments-SDK-Android/2f022fe6f242486cc000a1a16ffa03df2def5049/sdk/src/main/res/drawable-xhdpi/cpsdk_button_tinkoff_pay.png -------------------------------------------------------------------------------- /sdk/src/main/res/drawable-xhdpi/cpsdk_ic_checkbox_checked.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudpayments/CloudPayments-SDK-Android/2f022fe6f242486cc000a1a16ffa03df2def5049/sdk/src/main/res/drawable-xhdpi/cpsdk_ic_checkbox_checked.png -------------------------------------------------------------------------------- /sdk/src/main/res/drawable-xhdpi/cpsdk_ic_checkbox_unchecked.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudpayments/CloudPayments-SDK-Android/2f022fe6f242486cc000a1a16ffa03df2def5049/sdk/src/main/res/drawable-xhdpi/cpsdk_ic_checkbox_unchecked.png -------------------------------------------------------------------------------- /sdk/src/main/res/drawable-xhdpi/cpsdk_ic_failure.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudpayments/CloudPayments-SDK-Android/2f022fe6f242486cc000a1a16ffa03df2def5049/sdk/src/main/res/drawable-xhdpi/cpsdk_ic_failure.png -------------------------------------------------------------------------------- /sdk/src/main/res/drawable-xhdpi/cpsdk_ic_progress.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudpayments/CloudPayments-SDK-Android/2f022fe6f242486cc000a1a16ffa03df2def5049/sdk/src/main/res/drawable-xhdpi/cpsdk_ic_progress.png -------------------------------------------------------------------------------- /sdk/src/main/res/drawable-xhdpi/cpsdk_ic_ps_jcb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudpayments/CloudPayments-SDK-Android/2f022fe6f242486cc000a1a16ffa03df2def5049/sdk/src/main/res/drawable-xhdpi/cpsdk_ic_ps_jcb.png -------------------------------------------------------------------------------- /sdk/src/main/res/drawable-xhdpi/cpsdk_ic_ps_mir.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudpayments/CloudPayments-SDK-Android/2f022fe6f242486cc000a1a16ffa03df2def5049/sdk/src/main/res/drawable-xhdpi/cpsdk_ic_ps_mir.png -------------------------------------------------------------------------------- /sdk/src/main/res/drawable-xhdpi/cpsdk_ic_success.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudpayments/CloudPayments-SDK-Android/2f022fe6f242486cc000a1a16ffa03df2def5049/sdk/src/main/res/drawable-xhdpi/cpsdk_ic_success.png -------------------------------------------------------------------------------- /sdk/src/main/res/drawable-xhdpi/drag_handle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudpayments/CloudPayments-SDK-Android/2f022fe6f242486cc000a1a16ffa03df2def5049/sdk/src/main/res/drawable-xhdpi/drag_handle.png -------------------------------------------------------------------------------- /sdk/src/main/res/drawable-xhdpi/ic_required_email.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudpayments/CloudPayments-SDK-Android/2f022fe6f242486cc000a1a16ffa03df2def5049/sdk/src/main/res/drawable-xhdpi/ic_required_email.png -------------------------------------------------------------------------------- /sdk/src/main/res/drawable-xxhdpi/cpsdk_button_tinkoff_pay.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudpayments/CloudPayments-SDK-Android/2f022fe6f242486cc000a1a16ffa03df2def5049/sdk/src/main/res/drawable-xxhdpi/cpsdk_button_tinkoff_pay.png -------------------------------------------------------------------------------- /sdk/src/main/res/drawable-xxhdpi/cpsdk_ic_checkbox_checked.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudpayments/CloudPayments-SDK-Android/2f022fe6f242486cc000a1a16ffa03df2def5049/sdk/src/main/res/drawable-xxhdpi/cpsdk_ic_checkbox_checked.png -------------------------------------------------------------------------------- /sdk/src/main/res/drawable-xxhdpi/cpsdk_ic_checkbox_unchecked.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudpayments/CloudPayments-SDK-Android/2f022fe6f242486cc000a1a16ffa03df2def5049/sdk/src/main/res/drawable-xxhdpi/cpsdk_ic_checkbox_unchecked.png -------------------------------------------------------------------------------- /sdk/src/main/res/drawable-xxhdpi/cpsdk_ic_failure.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudpayments/CloudPayments-SDK-Android/2f022fe6f242486cc000a1a16ffa03df2def5049/sdk/src/main/res/drawable-xxhdpi/cpsdk_ic_failure.png -------------------------------------------------------------------------------- /sdk/src/main/res/drawable-xxhdpi/cpsdk_ic_progress.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudpayments/CloudPayments-SDK-Android/2f022fe6f242486cc000a1a16ffa03df2def5049/sdk/src/main/res/drawable-xxhdpi/cpsdk_ic_progress.png -------------------------------------------------------------------------------- /sdk/src/main/res/drawable-xxhdpi/cpsdk_ic_ps_jcb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudpayments/CloudPayments-SDK-Android/2f022fe6f242486cc000a1a16ffa03df2def5049/sdk/src/main/res/drawable-xxhdpi/cpsdk_ic_ps_jcb.png -------------------------------------------------------------------------------- /sdk/src/main/res/drawable-xxhdpi/cpsdk_ic_ps_mir.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudpayments/CloudPayments-SDK-Android/2f022fe6f242486cc000a1a16ffa03df2def5049/sdk/src/main/res/drawable-xxhdpi/cpsdk_ic_ps_mir.png -------------------------------------------------------------------------------- /sdk/src/main/res/drawable-xxhdpi/cpsdk_ic_success.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudpayments/CloudPayments-SDK-Android/2f022fe6f242486cc000a1a16ffa03df2def5049/sdk/src/main/res/drawable-xxhdpi/cpsdk_ic_success.png -------------------------------------------------------------------------------- /sdk/src/main/res/drawable-xxhdpi/drag_handle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudpayments/CloudPayments-SDK-Android/2f022fe6f242486cc000a1a16ffa03df2def5049/sdk/src/main/res/drawable-xxhdpi/drag_handle.png -------------------------------------------------------------------------------- /sdk/src/main/res/drawable-xxhdpi/ic_required_email.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudpayments/CloudPayments-SDK-Android/2f022fe6f242486cc000a1a16ffa03df2def5049/sdk/src/main/res/drawable-xxhdpi/ic_required_email.png -------------------------------------------------------------------------------- /sdk/src/main/res/drawable-xxxhdpi/cpsdk_button_tinkoff_pay.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudpayments/CloudPayments-SDK-Android/2f022fe6f242486cc000a1a16ffa03df2def5049/sdk/src/main/res/drawable-xxxhdpi/cpsdk_button_tinkoff_pay.png -------------------------------------------------------------------------------- /sdk/src/main/res/drawable-xxxhdpi/cpsdk_ic_checkbox_checked.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudpayments/CloudPayments-SDK-Android/2f022fe6f242486cc000a1a16ffa03df2def5049/sdk/src/main/res/drawable-xxxhdpi/cpsdk_ic_checkbox_checked.png -------------------------------------------------------------------------------- /sdk/src/main/res/drawable-xxxhdpi/cpsdk_ic_checkbox_unchecked.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudpayments/CloudPayments-SDK-Android/2f022fe6f242486cc000a1a16ffa03df2def5049/sdk/src/main/res/drawable-xxxhdpi/cpsdk_ic_checkbox_unchecked.png -------------------------------------------------------------------------------- /sdk/src/main/res/drawable-xxxhdpi/cpsdk_ic_failure.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudpayments/CloudPayments-SDK-Android/2f022fe6f242486cc000a1a16ffa03df2def5049/sdk/src/main/res/drawable-xxxhdpi/cpsdk_ic_failure.png -------------------------------------------------------------------------------- /sdk/src/main/res/drawable-xxxhdpi/cpsdk_ic_progress.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudpayments/CloudPayments-SDK-Android/2f022fe6f242486cc000a1a16ffa03df2def5049/sdk/src/main/res/drawable-xxxhdpi/cpsdk_ic_progress.png -------------------------------------------------------------------------------- /sdk/src/main/res/drawable-xxxhdpi/cpsdk_ic_ps_jcb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudpayments/CloudPayments-SDK-Android/2f022fe6f242486cc000a1a16ffa03df2def5049/sdk/src/main/res/drawable-xxxhdpi/cpsdk_ic_ps_jcb.png -------------------------------------------------------------------------------- /sdk/src/main/res/drawable-xxxhdpi/cpsdk_ic_ps_mir.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudpayments/CloudPayments-SDK-Android/2f022fe6f242486cc000a1a16ffa03df2def5049/sdk/src/main/res/drawable-xxxhdpi/cpsdk_ic_ps_mir.png -------------------------------------------------------------------------------- /sdk/src/main/res/drawable-xxxhdpi/cpsdk_ic_success.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudpayments/CloudPayments-SDK-Android/2f022fe6f242486cc000a1a16ffa03df2def5049/sdk/src/main/res/drawable-xxxhdpi/cpsdk_ic_success.png -------------------------------------------------------------------------------- /sdk/src/main/res/drawable-xxxhdpi/drag_handle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudpayments/CloudPayments-SDK-Android/2f022fe6f242486cc000a1a16ffa03df2def5049/sdk/src/main/res/drawable-xxxhdpi/drag_handle.png -------------------------------------------------------------------------------- /sdk/src/main/res/drawable-xxxhdpi/ic_required_email.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudpayments/CloudPayments-SDK-Android/2f022fe6f242486cc000a1a16ffa03df2def5049/sdk/src/main/res/drawable-xxxhdpi/ic_required_email.png -------------------------------------------------------------------------------- /sdk/src/main/res/drawable/cpsdk_bg_button_close.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 8 | 9 | 12 | -------------------------------------------------------------------------------- /sdk/src/main/res/drawable/cpsdk_bg_button_gp.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /sdk/src/main/res/drawable/cpsdk_bg_button_gp_black.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /sdk/src/main/res/drawable/cpsdk_bg_edit_text.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 14 | -------------------------------------------------------------------------------- /sdk/src/main/res/drawable/cpsdk_bg_edit_text_error.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 14 | -------------------------------------------------------------------------------- /sdk/src/main/res/drawable/cpsdk_bg_edit_text_focused.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 14 | -------------------------------------------------------------------------------- /sdk/src/main/res/drawable/cpsdk_bg_edit_text_selector.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /sdk/src/main/res/drawable/cpsdk_bg_edit_text_selector_error.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /sdk/src/main/res/drawable/cpsdk_bg_popup.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /sdk/src/main/res/drawable/cpsdk_bg_rounded_black_button.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 12 | -------------------------------------------------------------------------------- /sdk/src/main/res/drawable/cpsdk_bg_rounded_blue_button.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 12 | -------------------------------------------------------------------------------- /sdk/src/main/res/drawable/cpsdk_bg_rounded_bottom_sheet.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 10 | -------------------------------------------------------------------------------- /sdk/src/main/res/drawable/cpsdk_bg_rounded_dialog.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 9 | -------------------------------------------------------------------------------- /sdk/src/main/res/drawable/cpsdk_bg_rounded_dialog_inset.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | -------------------------------------------------------------------------------- /sdk/src/main/res/drawable/cpsdk_bg_rounded_white_button_with_border.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 12 | 13 | 16 | -------------------------------------------------------------------------------- /sdk/src/main/res/drawable/cpsdk_edit_text_underline.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /sdk/src/main/res/drawable/cpsdk_edit_text_underline_error.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /sdk/src/main/res/drawable/cpsdk_googlepay_button_content.xml: -------------------------------------------------------------------------------- 1 | 6 | 12 | 18 | 24 | 30 | 36 | 42 | 48 | 49 | -------------------------------------------------------------------------------- /sdk/src/main/res/drawable/cpsdk_googlepay_button_overlay.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /sdk/src/main/res/drawable/cpsdk_ic_blue_circle.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 8 | 9 | 12 | -------------------------------------------------------------------------------- /sdk/src/main/res/drawable/cpsdk_ic_checkbox_selector.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /sdk/src/main/res/drawable/cpsdk_ic_close.xml: -------------------------------------------------------------------------------- 1 | 6 | 10 | 11 | -------------------------------------------------------------------------------- /sdk/src/main/res/drawable/cpsdk_ic_ps_maestro.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | 15 | 16 | -------------------------------------------------------------------------------- /sdk/src/main/res/drawable/cpsdk_ic_ps_mastercard.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | 15 | 16 | -------------------------------------------------------------------------------- /sdk/src/main/res/drawable/cpsdk_ic_ps_troy.xml: -------------------------------------------------------------------------------- 1 | 6 | 10 | 14 | 15 | -------------------------------------------------------------------------------- /sdk/src/main/res/drawable/cpsdk_ic_ps_visa.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | 16 | 19 | 22 | 23 | -------------------------------------------------------------------------------- /sdk/src/main/res/drawable/cpsdk_ic_save_card_popup.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | 15 | 16 | -------------------------------------------------------------------------------- /sdk/src/main/res/drawable/cpsdk_ic_scan.xml: -------------------------------------------------------------------------------- 1 | 6 | 10 | 11 | -------------------------------------------------------------------------------- /sdk/src/main/res/font/rubik.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /sdk/src/main/res/font/rubik_black.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudpayments/CloudPayments-SDK-Android/2f022fe6f242486cc000a1a16ffa03df2def5049/sdk/src/main/res/font/rubik_black.ttf -------------------------------------------------------------------------------- /sdk/src/main/res/font/rubik_black_italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudpayments/CloudPayments-SDK-Android/2f022fe6f242486cc000a1a16ffa03df2def5049/sdk/src/main/res/font/rubik_black_italic.ttf -------------------------------------------------------------------------------- /sdk/src/main/res/font/rubik_bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudpayments/CloudPayments-SDK-Android/2f022fe6f242486cc000a1a16ffa03df2def5049/sdk/src/main/res/font/rubik_bold.ttf -------------------------------------------------------------------------------- /sdk/src/main/res/font/rubik_bold_italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudpayments/CloudPayments-SDK-Android/2f022fe6f242486cc000a1a16ffa03df2def5049/sdk/src/main/res/font/rubik_bold_italic.ttf -------------------------------------------------------------------------------- /sdk/src/main/res/font/rubik_italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudpayments/CloudPayments-SDK-Android/2f022fe6f242486cc000a1a16ffa03df2def5049/sdk/src/main/res/font/rubik_italic.ttf -------------------------------------------------------------------------------- /sdk/src/main/res/font/rubik_light.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudpayments/CloudPayments-SDK-Android/2f022fe6f242486cc000a1a16ffa03df2def5049/sdk/src/main/res/font/rubik_light.ttf -------------------------------------------------------------------------------- /sdk/src/main/res/font/rubik_light_italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudpayments/CloudPayments-SDK-Android/2f022fe6f242486cc000a1a16ffa03df2def5049/sdk/src/main/res/font/rubik_light_italic.ttf -------------------------------------------------------------------------------- /sdk/src/main/res/font/rubik_medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudpayments/CloudPayments-SDK-Android/2f022fe6f242486cc000a1a16ffa03df2def5049/sdk/src/main/res/font/rubik_medium.ttf -------------------------------------------------------------------------------- /sdk/src/main/res/font/rubik_medium_italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudpayments/CloudPayments-SDK-Android/2f022fe6f242486cc000a1a16ffa03df2def5049/sdk/src/main/res/font/rubik_medium_italic.ttf -------------------------------------------------------------------------------- /sdk/src/main/res/font/rubik_regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudpayments/CloudPayments-SDK-Android/2f022fe6f242486cc000a1a16ffa03df2def5049/sdk/src/main/res/font/rubik_regular.ttf -------------------------------------------------------------------------------- /sdk/src/main/res/font/sf_pro_text_regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudpayments/CloudPayments-SDK-Android/2f022fe6f242486cc000a1a16ffa03df2def5049/sdk/src/main/res/font/sf_pro_text_regular.ttf -------------------------------------------------------------------------------- /sdk/src/main/res/layout/activity_cpsdk_payment.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 15 | 16 | 21 | -------------------------------------------------------------------------------- /sdk/src/main/res/layout/dialog_cpsdk_payment_process.xml: -------------------------------------------------------------------------------- 1 | 2 | 13 | 14 | 22 | 23 | 34 | 35 | 50 | 51 |