package com.futo.futopay import android.util.Log import android.view.ViewGroup import androidx.fragment.app.Fragment import androidx.lifecycle.lifecycleScope import com.google.gson.JsonObject import com.google.gson.JsonParser import com.stripe.android.PaymentConfiguration import com.stripe.android.paymentsheet.PaymentSheet import com.stripe.android.paymentsheet.PaymentSheetResult import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import java.io.BufferedReader import java.io.InputStreamReader import java.net.HttpURLConnection import java.net.URL class PaymentManager { private val _fragment: Fragment; private val _overlayContainer: ViewGroup; private val _sheet: PaymentSheet; private val _paymentState: PaymentState; constructor(paymentState: PaymentState, fragment: Fragment, overlayContainer: ViewGroup, onCompleted: (success: Boolean, exception: Throwable?)->Unit) { _fragment = fragment; _paymentState = paymentState; _overlayContainer = overlayContainer; _sheet = PaymentSheet(_fragment) { paymentSheetResult -> when(paymentSheetResult) { is PaymentSheetResult.Canceled -> { } is PaymentSheetResult.Failed -> { onCompleted(false, paymentSheetResult.error) } is PaymentSheetResult.Completed -> { onCompleted(true, null); } } }; } fun startPayment(paymentState: PaymentState, scope: CoroutineScope, productId: String) { scope.launch(Dispatchers.IO){ try{ val availableCurrencies = _paymentState.getAvailableCurrencies(productId); val country = getCountryFromIP()?.let { c -> PaymentConfigurations.COUNTRIES.find { it.id.equals(c, ignoreCase = true) } }; withContext(Dispatchers.Main) { SlideUpPayment.startPayment(paymentState, _overlayContainer, productId, country, availableCurrencies) { method, request -> when(method) { "stripe" -> startPaymentStripe(productId, request.currency, request.mail, request.country, request.zipcode); } }; } } catch(ex: Throwable) { Log.e(TAG, "startPayment failed", ex); scope.launch(Dispatchers.Main){ UIDialogs.showGeneralErrorDialog(_fragment.requireContext(), "Failed to get required payment data", ex); } } } } private fun startPaymentStripe(productId: String, currency: String, email: String, country: String? = null, zipcode: String? = null) { _fragment.lifecycleScope.launch(Dispatchers.IO) { try { Log.i("BuyFragment", "Starting payment"); val paymentIntentResult = _paymentState.getPaymentIntent(productId, currency, email, country, zipcode); val customerConfig = if(paymentIntentResult.customer != null && paymentIntentResult.ephemeralKey != null) PaymentSheet.CustomerConfiguration(paymentIntentResult.customer, paymentIntentResult.ephemeralKey); else null; PaymentConfiguration.init(_fragment.requireContext(), paymentIntentResult.publishableKey); withContext(Dispatchers.Main) { _sheet.presentWithPaymentIntent( paymentIntentResult.paymentIntent, PaymentSheet.Configuration( merchantDisplayName = "FUTO", customer = customerConfig, allowsDelayedPaymentMethods = true, defaultBillingDetails = PaymentSheet.BillingDetails(PaymentSheet.Address(country = country, postalCode = zipcode), email), billingDetailsCollectionConfiguration = PaymentSheet.BillingDetailsCollectionConfiguration( email = PaymentSheet.BillingDetailsCollectionConfiguration.CollectionMode.Always ) ) ); } } catch(ex: Throwable) { Log.e(TAG, "Payment failed: ${ex.message}", ex); withContext(Dispatchers.Main) { UIDialogs.showGeneralErrorDialog(_fragment.requireContext(), "Payment failed\nIf you are charged you should always receive the key in your mail.", ex); } } } } //TODO: Determine a good provider private fun getCountryFromIP(): String? { val urlString = "https://freeipapi.com/api/json" val url = URL(urlString) val connection = url.openConnection() as HttpURLConnection connection.requestMethod = "GET" val reader = BufferedReader(InputStreamReader(connection.inputStream)) val response = StringBuilder() var line: String? while (reader.readLine().also { line = it } != null) { response.append(line) } reader.close() val json = response.toString(); val ipInfoObj = JsonParser.parseString(json) as JsonObject; if(ipInfoObj.has("countryCode")) return ipInfoObj.get("countryCode").asString; return null; } data class PaymentRequest( val productId: String, val currency: String, val mail: String, val country: String? = null, val zipcode: String? = null ); companion object { private const val TAG = "PaymentManager" } }