精华内容
下载资源
问答
  • stripe 海外支付

    千次阅读 2016-11-11 17:46:24
    stripe支付的核心代码import UIKit import Stripeclass CheckoutViewController: UIViewController, STPPaymentContextDelegate { // 1) To get started with this demo, first head to ...

    stripe支付的核心代码

    import UIKit
    import Stripe
    
    class CheckoutViewController: UIViewController, STPPaymentContextDelegate {
    
        // 1) To get started with this demo, first head to https://dashboard.stripe.com/account/apikeys
        // and copy your "Test Publishable Key" (it looks like pk_test_abcdef) into the line below.
    
        let stripePublishableKey = "pk_test_8LwPyj7Srlke48c6MIohzaGW"
    
        // 2) Next, optionally, to have this demo save your user's payment details, head to
        // https://github.com/stripe/example-ios-backend , click "Deploy to Heroku", and follow
        // the instructions (don't worry, it's free). Replace nil on the line below with your
    
        let backendBaseURL: String? = nil //"http://:8083/pay.json?"
    
        // 3) Optionally, to enable Apple Pay, follow the instructions at https://stripe.com/docs/mobile/apple-pay
        // to create an Apple Merchant ID. Replace nil on the line below with it (it looks like merchant.com.yourappname).
    
        let appleMerchantID: String? = nil
    
        // These values will be shown to the user when they purchase with Apple Pay.
        let companyName = "Emoji Apparel"
        let paymentCurrency = "usd"
    
        let paymentContext: STPPaymentContext
    
        let theme: STPTheme
        let paymentRow: CheckoutRowView
        let totalRow: CheckoutRowView
        let buyButton: BuyButton
        let rowHeight: CGFloat = 44
        let productImage = UILabel()
        let activityIndicator = UIActivityIndicatorView(activityIndicatorStyle: .gray)
        var product = ""
        var paymentInProgress: Bool = false {
            didSet {
                UIView.animate(withDuration: 0.3, delay: 0, options: .curveEaseIn, animations: {
                    if self.paymentInProgress {
                        self.activityIndicator.startAnimating()
                        self.activityIndicator.alpha = 1
                        self.buyButton.alpha = 0
                    }
                    else {
                        self.activityIndicator.stopAnimating()
                        self.activityIndicator.alpha = 0
                        self.buyButton.alpha = 1
                    }
                    }, completion: nil)
            }
        }
    
        init(product: String, price: Int, settings: Settings) {
            self.product = product
            self.productImage.text = product
            self.theme = settings.theme
            self.paymentRow = CheckoutRowView(title: "Payment", detail: "Select Payment",
                                              theme: settings.theme)
            self.totalRow = CheckoutRowView(title: "Total", detail: "", tappable: false,
                                            theme: settings.theme)
            self.buyButton = BuyButton(enabled: true, theme: settings.theme)
            MyAPIClient.sharedClient.baseURLString = self.backendBaseURL
    
            // This code is included here for the sake of readability, but in your application you should set up your configuration and theme earlier, preferably in your App Delegate.
    
            let config = STPPaymentConfiguration.shared()
            config.publishableKey = self.stripePublishableKey
            config.appleMerchantIdentifier = self.appleMerchantID
            config.companyName = self.companyName
            config.requiredBillingAddressFields = settings.requiredBillingAddressFields
            config.additionalPaymentMethods = settings.additionalPaymentMethods
            config.smsAutofillDisabled = !settings.smsAutofillEnabled
    
            let paymentContext = STPPaymentContext(apiAdapter: MyAPIClient.sharedClient,
                                                   configuration: config,
                                                   theme: settings.theme)
            let userInformation = STPUserInformation()
            paymentContext.prefilledInformation = userInformation
    
            paymentContext.paymentAmount = price
            paymentContext.paymentCurrency = self.paymentCurrency
    
            self.paymentContext = paymentContext
            super.init(nibName: nil, bundle: nil)
            self.paymentContext.delegate = self
            paymentContext.hostViewController = self
        }
    
        required init?(coder aDecoder: NSCoder) {
            fatalError("init(coder:) has not been implemented")
        }
    
        override func viewDidLoad() {
            super.viewDidLoad()
            self.view.backgroundColor = self.theme.primaryBackgroundColor
            var red: CGFloat = 0
            self.theme.primaryBackgroundColor.getRed(&red, green: nil, blue: nil, alpha: nil)
            self.activityIndicator.activityIndicatorViewStyle = red < 0.5 ? .white : .gray
            self.navigationItem.title = "Emoji Apparel"
    
            self.productImage.font = UIFont.systemFont(ofSize: 70)
            self.view.addSubview(self.totalRow)
            self.view.addSubview(self.paymentRow)
            self.view.addSubview(self.productImage)
            self.view.addSubview(self.buyButton)
            self.view.addSubview(self.activityIndicator)
            self.activityIndicator.alpha = 0
            self.buyButton.addTarget(self, action: #selector(didTapBuy), for: .touchUpInside)
            self.totalRow.detail = "$\(self.paymentContext.paymentAmount/100).00"
            self.paymentRow.onTap = { [weak self] _ in
                self?.paymentContext.pushPaymentMethodsViewController()
            }
        }
    
        override func viewDidLayoutSubviews() {
            super.viewDidLayoutSubviews()
            let width = self.view.bounds.width
            self.productImage.sizeToFit()
            self.productImage.center = CGPoint(x: width/2.0,
                                                   y: self.productImage.bounds.height/2.0 + rowHeight)
            self.paymentRow.frame = CGRect(x: 0, y: self.productImage.frame.maxY + rowHeight,
                                               width: width, height: rowHeight)
            self.totalRow.frame = CGRect(x: 0, y: self.paymentRow.frame.maxY,
                                             width: width, height: rowHeight)
            self.buyButton.frame = CGRect(x: 0, y: 0, width: 88, height: 44)
            self.buyButton.center = CGPoint(x: width/2.0, y: self.totalRow.frame.maxY + rowHeight*1.5)
            self.activityIndicator.center = self.buyButton.center
        }
    
        func didTapBuy() {
            self.paymentInProgress = true
            self.paymentContext.requestPayment()
        }
    
        func paymentContext(_ paymentContext: STPPaymentContext, didCreatePaymentResult paymentResult: STPPaymentResult, completion: @escaping STPErrorBlock) {
            MyAPIClient.sharedClient.completeCharge(paymentResult, amount: self.paymentContext.paymentAmount,
                                                    completion: completion)
        }
    
        func paymentContext(_ paymentContext: STPPaymentContext, didFinishWith status: STPPaymentStatus, error: Error?) {
            self.paymentInProgress = false
            let title: String
            let message: String
            switch status {
            case .error:
                title = "Error"
                message = error?.localizedDescription ?? ""
            case .success:
                title = "Success"
                message = "You bought a \(self.product)!"
            case .userCancellation:
                return
            }
            let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert)
            let action = UIAlertAction(title: "OK", style: .default, handler: nil)
            alertController.addAction(action)
            self.present(alertController, animated: true, completion: nil)
        }
    
        // MARK: STPPaymentContextDelegate
    
        func paymentContextDidChange(_ paymentContext: STPPaymentContext) {
            self.paymentRow.loading = paymentContext.loading
            if let paymentMethod = paymentContext.selectedPaymentMethod {
                self.paymentRow.detail = paymentMethod.label
            }
            else {
                self.paymentRow.detail = "Select Payment"
            }
        }
    
        func paymentContext(_ paymentContext: STPPaymentContext, didFailToLoadWithError error: Error) {
            let alertController = UIAlertController(
                title: "Error",
                message: error.localizedDescription,
                preferredStyle: .alert
            )
            let cancel = UIAlertAction(title: "Cancel", style: .cancel, handler: { action in
                // Need to assign to _ because optional binding loses @discardableResult value
                // https://bugs.swift.org/browse/SR-1681
                _ = self.navigationController?.popViewController(animated: true)
            })
            let retry = UIAlertAction(title: "Retry", style: .default, handler: { action in
                self.paymentContext.retryLoading()
            })
            alertController.addAction(cancel)
            alertController.addAction(retry)
            self.present(alertController, animated: true, completion: nil)
        }
    
    }
    

    其实只是复制了下官方demo的代码。。。
    官网地址:https://stripe.com/docs/mobile/ios
    测试数据:https://stripe.com/docs/testing



    对于前端来说,集成stripe进行支付的大体流程如下:
    pod install 。。。
    配置stripe:
    1)
    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    if (StripePublishableKey) {
    [Stripe setDefaultPublishableKey:StripePublishableKey];
    }
    return YES;
    }

    2)
    获取stripe的支付token(iOS端基本就是几个代理方法就OK)
    3)
    将所选商品总价、tokenId 等后台所需数据配置好post给自己的服务端(真正的支付操作),让自己的服务端去请求 stripeServer 端
    4)
    自己的服务端会根据stripeserver端的请求结果返回支付状态,前端根据该返回值进行相应处理即可

    关键代码在 这篇文章,如token的创建,chage的生成等

    展开全文
  • <!... <... <head>...meta charset="utf-8">...meta name="viewport" content="width=device-width,initial-scale=...测试stripe支付demo</title> </head> <body> <div class=..
     
    <!DOCTYPE html>
    <html>
     
    	<head>
    		<meta charset="utf-8">
    		<meta name="viewport" content="width=device-width,initial-scale=1.0">
    		<title>测试stripe支付demo</title>
    	</head>
     
    	<body>
    		
    		<div class="form-row">
    			<label for="card-element">
    				Credit or debit card
    			</label>
    			<div style="margin-top: 10px;"><input id="cardholder-name" type="text" placeholder="持卡人姓名"></div>
     
    			<div id="card-element"></div>
    			<!-- Used to display form errors. -->
    			<div id="card-errors" role="alert"></div>
    			<button id="card-button" class="btnClass">确认支付</button>
    		</div>
    		
    	</body>
    	<style type="text/css">
    		#card-errors{
    			color: red;
    		}
    	</style>
    	<script src="https://js.stripe.com/v3/"></script>
    	<script>
    		var stripe = Stripe('pk_test_E5K4sB4KOMNl1WRk4kVtWdGI00tF9nbt75');
    		var elements = stripe.elements();
    		var cardElement = elements.create('card');
    		cardElement.mount('#card-element');
    		var cardholderName = document.getElementById('cardholder-name');
    		var cardButton = document.getElementById('card-button');
    		var clientSecret = 'pi_1FRsD4IOkWpKGmkzELjtmxJx_secret_WBTa9EgTKBFY9hTZ444smnVIF'; //后台传的密钥
    		console.log(clientSecret)
    		cardElement.addEventListener('change', function(event) {
    		  var displayError = document.getElementById('card-errors');
    		  if (event.error) {
    		    displayError.textContent = event.error.message;
    		  } else {
    		    displayError.textContent = '';
    		  }
    		});
                
            cardButton.addEventListener('click', function(ev) {
            stripe.handleCardPayment(
                clientSecret, cardElement, {
                payment_method_data: {
                    billing_details: {name: cardholderName.value}, //持卡人姓名
                    // Authorization:'Bearer' +"pi_1FRsD4IOkWpKGmkzELjtmxJx_secret_WBTa9EgTKBFY9hTZ444smnVIF"
                },
                
                }
            ).then(function(result) {
                console.log(result)
                if (result.error) {
                    // f付款失败
                } else {
                    // 付款成功
                }
            });
            });
    	
    		
    	</script>
    	<style>
    		#card-element {
    			padding: 30px 0px;
    		}
     
    		.btnClass {
    			width: 100px;
    			height: 28px;
    			line-height: 28px;
    			background-color: burlywood;
    			border: none;
    			margin-left: 20%;
    			margin-top: 20px;
    		}
            .StripeElement {
                box-sizing: border-box;
     
                height: 40px;
     
                padding: 10px 12px;
     
                border: 1px solid transparent;
                border-radius: 4px;
                background-color: white;
     
                box-shadow: 0 1px 3px 0 #e6ebf1;
                -webkit-transition: box-shadow 150ms ease;
                transition: box-shadow 150ms ease;
            }
     
            .StripeElement--focus {
            box-shadow: 0 1px 3px 0 #cfd7df;
            }
     
            .StripeElement--invalid {
            border-color: #fa755a;
            }
     
            .StripeElement--webkit-autofill {
            background-color: #fefde5 !important;
                    }
    	</style>
    
    展开全文
  • 美国Stripe支付Android端集成流程

    千次阅读 2016-11-20 13:36:05
    至此,Stripe支付就接入完成了,具体的扣费则是需要自己服务器与stripe服务器交互,在提交完支付信息再得到支付结果过程中进行加载框的处理就看这里就不再多说了。 下面我把我这个类的完整代码放出来吧: package...

    上家公司想要拓展自己在新加坡的市场,打算做一个新加坡本地的生活服务应用,其中少不了的就是支付了。国外支付这块一直是个头疼的问题。想用Google Wallet吧,但它是采用NFC接触式交易,想要进行线上服务时没法进行,后来就去整个贝宝PayPal支付。在这里想吐槽一下,PayPal支付做起来真是头疼,文档阅读起来很吃力,官方也没什么好的demo,无奈,在度娘里不断地搜索,找到了别人做的一个demo,我自己拿来集成了一下,终于弄好了。好了是好了,不过后来老板又重新定了需求,因为嫌PayPal支付的手续费太贵,所以果断弃用,不断打听了解,找到了这个Stripe支付。又是第一次接触这个支付,我呢就把Stripe支付的流程用博客记录下来,以备不时之需,有想要在google play上架自己带支付的应用的小伙伴们也可以参考一下这篇文章。

    Stripe Android开发文档地址: https://stripe.com/docs/mobile/android

    第一步,在build.gradle中配置sdk:

    compile ‘com.stripe:stripe-android:3.0.1’

    要在Eclipse上安装Stripe引用,你需要 :
    1. 首先下载 stripe-android 库.
    2. 确保你的 Android SDK 最低在 Level 17 以上并且有android-support-v4 包.
    3. 导入 stripe 文件夹 到 Eclipse.
    4. 在你项目设置里, 在“Android”类别下的“Stripe”部分添加项目依赖库。

    如果说没有找到jar包的下载地址,可以用as添加主module依赖:
    compile ‘com.stripe:stripe-android:3.0.1’ ,然后在在这里拷贝出来:
    这里写图片描述

    第二步,收集信用卡信息

    这里需要注意的是,Stripe并没有支付界面,收集信用卡信息的界面需要自己设计。

    使用前先导入stripe支付应用的类:

    import com.stripe.android.*;
    这里会用到两大主类:CardStripe

    我们使用用户的输入信息来初始化一个Card:

    Card card = new Card(
      cardNumber, //卡号
      cardExpMonth, //卡片过期月份
      cardExpYear, //卡片过期年份
      cardCVC //CVC验证码
    );
    card.validateNumber(); //检测卡号是否有效
    card.validateCVC(); //检测CVC验证码是否有效
    

    第三步,构建Stripe生成token:

    if (card.validateCard()) {
         Stripe stripe = new Stripe();
         //调用创建token方法
         stripe.createToken(
          card,//传入card对象
          new TokenCallback() {
            //这里的token打印出来是一串json数据,其中的    token需要用getId()来得到
            public void onSuccess(Token token) {
               // 这里生成得到了token,你需要将它发送到自己服务器,然后服务器利用这个token和支付金额去向    Stripe请求扣费
               submitPaymentInfo(token.getId(),"12.20");//提交支付信息
            }
            public void onError(Exception error) {
              // 显示本地错误信息
              Toast.makeText(getContext(),
                error.getLocalizedString(getContext()),
                Toast.LENGTH_LONG
              ).show();
            }
          }
        )
    }else {//卡号有误
                MToast.shortToast("The card number that you entered is invalid");
            } else if (!card.validateExpiryDate()) {//过期时间有误
                MToast.shortToast("The expiration date that you entered is invalid");
            } else if (!card.validateCVC()) {//CVC验证码有误
                MToast.shortToast("The CVC code that you entered is invalid");
            } else {//卡片详情有误
                MToast.shortToast("The card details that you entered are invalid");
            }
        }

    至此,Stripe支付就接入完成了,具体的扣费则是需要自己服务器与stripe服务器交互,在提交完支付信息再得到支付结果过程中进行加载框的处理就看这里就不再多说了。
    下面我把我这个类的完整代码放出来吧:

    package com.anmu.wannasg.user.ui;
    
    import android.content.Intent;
    import android.graphics.drawable.BitmapDrawable;
    import android.support.v4.app.DialogFragment;
    import android.text.Editable;
    import android.text.TextUtils;
    import android.text.TextWatcher;
    import android.view.Gravity;
    import android.view.LayoutInflater;
    import android.view.View;
    import android.view.ViewGroup;
    import android.widget.EditText;
    import android.widget.ImageView;
    import android.widget.LinearLayout;
    import android.widget.PopupWindow;
    import android.widget.RelativeLayout;
    import android.widget.TextView;
    import android.widget.Toast;
    
    import com.alibaba.fastjson.JSONObject;
    import com.android.volley.AuthFailureError;
    import com.android.volley.Request;
    import com.android.volley.Response;
    import com.android.volley.VolleyError;
    import com.android.volley.toolbox.StringRequest;
    import com.anmu.wannasg.R;
    import com.anmu.wannasg.SwipeBack.SwipeBackLayout;
    import com.anmu.wannasg.application.MyApplication;
    import com.anmu.wannasg.autolayout.utils.AutoUtils;
    import com.anmu.wannasg.commonactivity.BaseSwipeBackActivity;
    import com.anmu.wannasg.net.Keys;
    import com.anmu.wannasg.net.URLS;
    import com.anmu.wannasg.utils.L;
    import com.anmu.wannasg.utils.MToast;
    import com.anmu.wannasg.utils.ScreenUtils;
    import com.anmu.wannasg.widget.pickerview.NumberPickerView;
    import com.ant.liao.GifView;
    import com.stripe.android.Stripe;
    import com.stripe.android.TokenCallback;
    import com.stripe.android.model.Card;
    import com.stripe.android.model.Token;
    
    import java.util.HashMap;
    import java.util.Map;
    
    /**
     * Created by Maibenben on 2016/10/27.
     */
    
    public class UEPaymentInputActivity extends BaseSwipeBackActivity {
    
        private RelativeLayout top;
        private ImageView btnBack;
        private LinearLayout middle;
        private EditText edtCardHolderName;
        private EditText edtCardNumber;
        private EditText edtCvv;
        //    private EditText edtExpiryDate;
        private LinearLayout bottom;
        private NumberPickerView picker_year, picker_month;
        private TextView btnReset;
        private TextView btnNext;
        private TextView txt_price;
    
        String amount = "";
    
        String holdername = "", cardnumber = "", cvv = "", expirydate = "";
    
        Intent mIntent;
    
        String code;
    
        int type = 0;
    
        String[] months = {"01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "12"};
        String[] years = {"2016", "2017", "2018", "2019", "2020", "2021", "2022", "2023", "2024", "2025", "2026", "2027"
                , "2028", "2029", "2030", "2031", "2032", "2033", "2034", "2035", "2036", "2037", "2038", "2039"};
    
        @Override
        public void setContentView() {
            mIntent = getIntent();
            if (mIntent != null) {
                amount = mIntent.getStringExtra("amount");
                code = mIntent.getStringExtra("code");
                type = mIntent.getIntExtra("type", 0);
            }
            setContentView(R.layout.u_activity_payment_input);
        }
    
        @Override
        public void initData() {
            picker_year.refreshByNewDisplayedValues(years);
            picker_month.refreshByNewDisplayedValues(months);
            initPopuptWindow();
            txt_price.setText("S$ " + amount);
        }
    
        @Override
        public void initWidget() {
    
            SwipeBackLayout swipeBackLayout = getSwipeBackLayout();
            swipeBackLayout.setEdgeSize(ScreenUtils.getScreenWidth(this) / 10);
            swipeBackLayout.setEdgeTrackingEnabled(MyApplication.getInstance().getCloseModel());
    
            top = (RelativeLayout) findViewById(R.id.top);
            btnBack = (ImageView) findViewById(R.id.btn_back);
            middle = (LinearLayout) findViewById(R.id.middle);
            edtCardHolderName = (EditText) findViewById(R.id.edt_card_holder_name);
            edtCardNumber = (EditText) findViewById(R.id.edt_card_number);
            edtCvv = (EditText) findViewById(R.id.edt_cvv);
    //        edtExpiryDate = (EditText) findViewById(R.id.edt_expiry_date);
            bottom = (LinearLayout) findViewById(R.id.bottom);
            picker_year = (NumberPickerView) findViewById(R.id.picker_year);
            picker_month = (NumberPickerView) findViewById(R.id.picker_month);
            btnReset = (TextView) findViewById(R.id.btn_reset);
            btnNext = (TextView) findViewById(R.id.btn_next);
            txt_price = (TextView) findViewById(R.id.txt_price);
    
            btnBack.setOnClickListener(this);
            btnReset.setOnClickListener(this);
            btnNext.setOnClickListener(this);
    
        }
    
        @Override
        public void widgetClick(View v) {
            switch (v.getId()) {
                case R.id.btn_back:
                    scrollToFinishActivity();
                    break;
                case R.id.btn_reset:
                    edtCardHolderName.setText("");
                    edtCardNumber.setText("");
                    edtCvv.setText("");
                    break;
                case R.id.btn_next:
    
                    holdername = edtCardHolderName.getText().toString().trim();
    
                    cardnumber = edtCardNumber.getText().toString().trim();
    
                    cvv = edtCvv.getText().toString().trim();
    
                    expirydate = picker_year.getContentByCurrValue() + picker_month.getContentByCurrValue();
    
                    if (TextUtils.isEmpty(holdername)) {
                        MToast.shortToast("please enter card holder name");
                        return;
                    }
                    if (TextUtils.isEmpty(cardnumber)) {
                        MToast.shortToast("please enter card number");
                        return;
                    }
                    if (TextUtils.isEmpty(cvv)) {
                        MToast.shortToast("please enter cvv");
                        return;
                    }
                    if (TextUtils.isEmpty(expirydate)) {
                        MToast.shortToast("please enter expiry date");
                        return;
                    }
                    mLoadingPopupWindow.showAtLocation(loadingView, Gravity.CENTER, 0, 0);
                    saveCreditCard(holdername, cardnumber, cvv, expirydate);
                    break;
            }
        }
    
        @Override
        public void getData() {
    
        }
    
        @Override
        public void dealData(String response) {
    
        }
    
        String url = "";
    
        private void getMyData(final Token token) {
            url = URLS.USER_STRIPE_PAYMENT;
            StringRequest submitToken = new StringRequest(Request.Method.POST, url, new Response.Listener<String>() {
                @Override
                public void onResponse(String response) {
                    dealSubmitResponse(response);
                }
            }, new Response.ErrorListener() {
                @Override
                public void onErrorResponse(VolleyError volleyError) {
                    if (mLoadingPopupWindow.isShowing()) {
                        mLoadingPopupWindow.dismiss();
                    }
                }
            }) {
                @Override
                protected Map<String, String> getParams() throws AuthFailureError {
                    Map<String, String> params = new HashMap<>();
                    params.put("stripeToken", token.getId());
                    params.put("code", code);
                    return params;
                }
            };
            MyApplication.getQueue().add(submitToken);
        }
    
        private void dealSubmitResponse(String response) {
            if (TextUtils.isEmpty(response)) {
                return;
            }
            JSONObject jsonObject = JSONObject.parseObject(response);
            int ret = jsonObject.getIntValue("ret");
            if (ret == 200) {
                //success
                MToast.shortToast("Payment Success");
                switch (type) {
                    case 2://pickup进来的
                        mIntent = new Intent(this, UPickUpWaitingDetailActivity.class);
                        mIntent.putExtra("code", code);
                        startActivity(mIntent);
                        finish();
                        overridePendingTransition(R.anim.slide_in_right, R.anim.slide_out_left);
                        break;
                    case 4://coupon进来的
                        mIntent = new Intent(this, UCouponSuccessfulPurchaseActivity.class);
                        mIntent.putExtra("code", code);
                        mIntent.putExtra("title", "Successful Purchase");
                        startActivity(mIntent);
                        finish();
                        overridePendingTransition(R.anim.slide_in_right, R.anim.slide_out_left);
                        break;
                }
            } else {
                MToast.shortToast("sorry,network or server error");
            }
            if (mLoadingPopupWindow.isShowing()) {
                mLoadingPopupWindow.dismiss();
            }
        }
    
        public void saveCreditCard(String holdername, String cardnumber, String cvv, String expirydate) {
            if (expirydate.length() < 6) {
                MToast.shortToast("Please enter correct expiry date");
                return;
            }
            Card card = new Card(cardnumber, Integer.parseInt(expirydate.substring(4, expirydate.length())), Integer.parseInt(expirydate.substring(0, 4)), cvv);
    //        card.setCurrency(form.getCurrency());
            boolean validation = card.validateCard();
            if (validation) {
                new Stripe().createToken(
                        card,
                        Keys.Stripe_Publishable_Key,
                        new TokenCallback() {
                            public void onSuccess(Token token) {
                                getMyData(token);
                                L.i(token.toString() + "\n---------\n" + token.getId());
                            }
    
                            public void onError(Exception error) {
                                L.i(error.toString());
                            }
                        });
            } else if (!card.validateNumber()) {
                if (mLoadingPopupWindow.isShowing()) {
                    mLoadingPopupWindow.dismiss();
                }
                MToast.shortToast("The card number that you entered is invalid");
            } else if (!card.validateExpiryDate()) {
                if (mLoadingPopupWindow.isShowing()) {
                    mLoadingPopupWindow.dismiss();
                }
                MToast.shortToast("The expiration date that you entered is invalid");
            } else if (!card.validateCVC()) {
                if (mLoadingPopupWindow.isShowing()) {
                    mLoadingPopupWindow.dismiss();
                }
                MToast.shortToast("The CVC code that you entered is invalid");
            } else {
                if (mLoadingPopupWindow.isShowing()) {
                    mLoadingPopupWindow.dismiss();
                }
                MToast.shortToast("The card details that you entered are invalid");
            }
        }
    
        GifView loadingGif;
        /**
         * popupwindow布局
         */
        View loadingView;
        PopupWindow mLoadingPopupWindow;
    
        /**
         * 创建PopupWindow
         */
        private void initPopuptWindow() {
            LayoutInflater layoutInflater = LayoutInflater.from(this);
            loadingView = layoutInflater.inflate(R.layout.u_popup_payemnt_loading, null);
            AutoUtils.auto(loadingView);
            //View loadingView ;
            //PopupWindow  mLoadingPopupWindow;
            // 创建一个PopupWindow
            // 参数1:contentView 指定PopupWindow的内容
            // 参数2:width 指定PopupWindow的width
            // 参数3:height 指定PopupWindow的height
            mLoadingPopupWindow = new PopupWindow(loadingView, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
    
            // 设置popwindow如果点击外面区域无法关闭。
            mLoadingPopupWindow.setOutsideTouchable(true);
            // 点击空白处时,隐藏掉pop窗口
            mLoadingPopupWindow.setFocusable(false);
            mLoadingPopupWindow.setBackgroundDrawable(new BitmapDrawable());
            //        backgroundAlpha(0.5f);// 设置背景为半透明
            //        mFilterPopupWindow.showAsDropDown(view_divide, 0, 0);
            loadingGif = (GifView) loadingView.findViewById(R.id.loading_gif);
            loadingGif.setGifImage(R.drawable.loadingview);
        }
    
    }
    

    xml代码:

    
    <?xml version="1.0" encoding="utf-8"?>
    <com.anmu.wannasg.autolayout.AutoRelativeLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@color/white"
        android:fitsSystemWindows="true"
        android:orientation="vertical">
    
        <RelativeLayout
            android:id="@+id/top"
            android:layout_width="match_parent"
            android:layout_height="81px">
    
            <ImageView
                android:id="@+id/btn_back"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:scaleType="fitXY"
                android:src="@mipmap/u_img_back_pink"/>
    
            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_centerInParent="true"
                android:gravity="center"
                android:text="ePayment"
                android:textColor="@color/black"
                android:textSize="35px"/>
    
            <View
                android:layout_width="match_parent"
                android:layout_height="1px"
                android:layout_alignParentBottom="true"
                android:background="@color/lightgrayline"/>
        </RelativeLayout>
    
        <LinearLayout
            android:id="@+id/middle"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_below="@+id/top"
            android:orientation="vertical"
            android:paddingBottom="30px"
            android:paddingLeft="30px"
            android:paddingRight="30px">
    
            <TextView
                android:layout_width="wrap_content"
                android:layout_height="85px"
                android:gravity="center_vertical"
                android:text="Card Holder Name:"
                android:textColor="@color/sixsix"
                android:textSize="30px"/>
    
            <EditText
                android:id="@+id/edt_card_holder_name"
                android:layout_width="match_parent"
                android:layout_height="70px"
                android:background="@drawable/u_edt_input_selector"
                android:maxEms="50"
                android:maxLines="1"
                android:paddingLeft="20px"
                android:paddingRight="20px"/>
    
            <TextView
                android:layout_width="wrap_content"
                android:layout_height="85px"
                android:gravity="center_vertical"
                android:text="Card Number:"
                android:textColor="@color/sixsix"
                android:textSize="30px"/>
    
            <EditText
                android:id="@+id/edt_card_number"
                android:layout_width="match_parent"
                android:layout_height="70px"
                android:background="@drawable/u_edt_input_selector"
                android:inputType="number"
                android:maxEms="50"
                android:maxLines="1"
                android:paddingLeft="20px"
                android:paddingRight="20px"/>
    
            <TextView
                android:layout_width="wrap_content"
                android:layout_height="85px"
                android:gravity="center_vertical"
                android:text="CVV:"
                android:textColor="@color/sixsix"
                android:textSize="30px"/>
    
            <EditText
                android:id="@+id/edt_cvv"
                android:layout_width="match_parent"
                android:layout_height="70px"
                android:background="@drawable/u_edt_input_selector"
                android:inputType="number"
                android:maxEms="50"
                android:maxLines="1"
                android:paddingLeft="20px"
                android:paddingRight="20px"/>
    
            <TextView
                android:layout_width="wrap_content"
                android:layout_height="85px"
                android:gravity="center_vertical"
                android:text="Expiry Date:"
                android:textColor="@color/sixsix"
                android:textSize="30px"/>
    
            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="100px"
                android:background="@drawable/u_edt_input_selector">
    
                <LinearLayout
                    android:layout_width="0dp"
                    android:layout_height="match_parent"
                    android:layout_weight="1"
                    android:gravity="center_vertical">
    
                    <TextView
                        android:layout_width="wrap_content"
                        android:layout_height="85px"
                        android:layout_marginLeft="20px"
                        android:layout_marginRight="40px"
                        android:gravity="center_vertical"
                        android:text="Month:"
                        android:textColor="@color/sixsix"
                        android:textSize="30px"/>
    
                    <com.anmu.wannasg.widget.pickerview.NumberPickerView
                        android:id="@+id/picker_month"
                        android:layout_width="100px"
                        android:layout_height="wrap_content"
                        app:npv_DividerColor="@color/no_color"
                        app:npv_ShowCount="1"
                        app:npv_TextColorHint="@color/sixsix"
                        app:npv_TextColorSelected="#1E64D2"
                        app:npv_TextSizeHint="14sp"
                        app:npv_TextSizeSelected="18sp"/>
    
                </LinearLayout>
    
                <LinearLayout
                    android:layout_width="0dp"
                    android:layout_height="match_parent"
                    android:layout_weight="1"
                    android:gravity="center_vertical">
    
                    <TextView
                        android:layout_width="wrap_content"
                        android:layout_height="85px"
                        android:layout_marginLeft="20px"
                        android:layout_marginRight="40px"
                        android:gravity="center_vertical"
                        android:text="Year:"
                        android:textColor="@color/sixsix"
                        android:textSize="30px"/>
    
                    <com.anmu.wannasg.widget.pickerview.NumberPickerView
                        android:id="@+id/picker_year"
                        android:layout_width="100px"
                        android:layout_height="wrap_content"
                        app:npv_DividerColor="@color/no_color"
                        app:npv_ShowCount="1"
                        app:npv_TextColorHint="@color/sixsix"
                        app:npv_TextColorSelected="#1E64D2"
                        app:npv_TextSizeHint="14sp"
                        app:npv_TextSizeSelected="18sp"/>
    
                </LinearLayout>
    
            </LinearLayout>
    
            <!--<EditText-->
            <!--android:id="@+id/edt_expiry_date"-->
            <!--android:layout_width="match_parent"-->
            <!--android:layout_height="70px"-->
            <!--android:background="@drawable/u_edt_input_selector"-->
            <!--android:hint="like 201601"-->
            <!--android:inputType="number"-->
            <!--android:maxEms="50"-->
            <!--android:maxLines="1"-->
            <!--android:paddingLeft="20px"-->
            <!--android:paddingRight="20px"-->
            <!--android:textColorHint="@color/CDCDCD"/>-->
    
        </LinearLayout>
    
        <RelativeLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_above="@id/bottom"
            android:layout_below="@+id/middle"
            android:background="@color/backgroudgray">
    
            <LinearLayout
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_alignParentRight="true"
                android:layout_margin="40px"
                android:orientation="horizontal"
                android:visibility="gone">
    
                <TextView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_marginRight="20px"
                    android:gravity="center"
                    android:text="Need pay"
                    android:textColor="@color/user_theme_color"
                    android:textSize="35px"/>
    
                <TextView
                    android:id="@+id/txt_price"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:gravity="center"
                    android:text="S$ 00"
                    android:textColor="@color/user_theme_color"
                    android:textSize="40px"/>
            </LinearLayout>
        </RelativeLayout>
        <!--<View-->
        <!--android:layout_width="match_parent"-->
        <!--android:layout_above="@id/bottom"-->
        <!--android:layout_below="@+id/middle"-->
        <!--android:background="@color/backgroudgray"-->
        <!--android:layout_height="match_parent"/>-->
    
        <LinearLayout
            android:id="@+id/bottom"
            android:layout_width="match_parent"
            android:layout_height="120px"
            android:layout_alignParentBottom="true"
            android:background="@color/user_theme_color"
            android:orientation="horizontal">
    
            <TextView
                android:id="@+id/btn_reset"
                android:layout_width="0dp"
                android:layout_height="match_parent"
                android:layout_weight="1"
                android:gravity="center"
                android:text="Reset"
                android:textColor="@color/white"
                android:textSize="35px"/>
    
            <View
                android:layout_width="1px"
                android:layout_height="match_parent"
                android:layout_marginBottom="20px"
                android:layout_marginTop="20px"
                android:background="@color/white"/>
    
            <TextView
                android:id="@+id/btn_next"
                android:layout_width="0dp"
                android:layout_height="match_parent"
                android:layout_weight="1"
                android:gravity="center"
                android:text="Next"
                android:textColor="@color/white"
                android:textSize="35px"/>
    
        </LinearLayout>
    
    </com.anmu.wannasg.autolayout.AutoRelativeLayout>
    展开全文
  • stripe.paymentMethods.create stripe.paymentMethods.create stripe.paymentIntents.create stripe.paymentIntents.create stripe.paymentIntents.confirm stripe.paymentIntents.confirm stripe.paymentIntents....

    谷歌flutter

    Google’s Flutter is an excellent platform and I like its capability to build robust hybrid apps the most. Flutter app development services can help entrepreneurs come up with feature-rich apps in a short time. As a Flutter app developer, I keep on exploring the possibilities of integrating innovative features and functionality in Flutter apps.

    Google的Flutter是一个出色的平台,我最喜欢其构建健壮的混合应用程序的能力。 Flutter应用程序开发服务可以帮助企业家在短时间内提出功能丰富的应用程序。 作为Flutter应用程序开发人员,我一直在探索在Flutter应用程序中集成创新功能的可能性。

    Recently I was searching How to integrate Google Pay using Stripe Payment Gateway in Flutter. I have also checked Stripe Official Documentation For Google Pay. But documentation is not for the Flutter Platform. So I decided to publish an article to integrate Google Pay using Stripe in Flutter.

    最近,我在搜索如何使用Flutter中的Stripe Payment Gateway集成Google Pay。 我还检查了Stripe的 Google Pay 官方文档 。 但是文档不适用于Flutter平台。 因此,我决定发表一篇文章,在Flutter中使用Stripe集成Google Pay。

    Stripe is the software platform for running an internet bussiness. It’s a suite of payment APIs that powers commerce for online businesses of all sizes.

    Stripe是用于运行Internet商务的软件平台。 这是一套支付API,可为各种规模的在线企业提供商务支持。

    Use of these platforms in this integration.

    在集成中使用这些平台。

    1. Flutter for Front-end Side Development.

      用于前端开发的Flutter

    2. Stripe to use its Payment APIs.

      条纹使用其付款API。

    3. Firebase to deploy Cloud Function for Server-Side Development.

      Firebase部署用于服务器端开发的云功能。

    Lets first make a note of the tasks we are going to do :

    让我们首先记下我们要完成的任务:

    1. Use Android Studio and Visual Studio Code as an Editor.

      使用Android Studio和Visual Studio代码作为编辑器。
    2. Create a new Flutter Project named as per your choice.

      根据您的选择创建一个新的Flutter项目。
    3. Use a stripe_native plugin.

      使用stripe_native插件。

    4. Create an Account on Stripe. Just Sign up and Sign in, not required other details as we will be using the test API keys.

      Stripe上创建一个帐户。 只需注册并登录,不需要其他详细信息,因为我们将使用测试API密钥。

    5. Create Flutter Project on Firebase Console and Upgrade your Firebase plan to Blaze Plan(Paid Plan). A paid account is the most important one.

      Firebase控制台上创建Flutter Project,并将Firebase计划升级到Blaze Plan(付费计划)。 付费帐户是最重要的帐户。

    6. Setup the Node.js environment in your system.

      在系统中设置Node .js环境。

    7. Setup Firebase CLI to deploy Firebase Cloud Functions.

      设置Firebase CLI以部署Firebase Cloud Functions。

    Source 资源

    Create a Flutter Project in your Android Studio or VS Code.

    在Android Studio或VS Code中创建Flutter项目。

    使用stripe_native插件 (Use stripe_native Plugin)

    Add this plugin into pubspec.yaml file.

    将此插件添加到pubspec.yaml文件中。

    dependencies:
      flutter:
        sdk: flutter
    
    
    
    
      cupertino_icons: ^0.1.3
      stripe_native: ^1.2.3

    Please run the following command

    请运行以下命令

    flutter pub get

    扑吧获得

    在条带上创建帐户 (Create Account on Stripe)

    Go to Stripe Website then perform Sign up and Sign In.

    转到Stripe网站,然后执行“注册并登录”。

    After Creating Stripe Account Go to Dashboard.

    创建Stripe Account后,转到Dashboard

    Image for post
    Source 资源

    Click on Get your test API keys. It gives you two API keys (Publishable Key, Secret key).

    单击获取您的测试API密钥。 它为您提供了两个API密钥(可发布密钥,秘密密钥)。

    Note : I have followed coding standards into this so when there is requirement to show details of particular word then its written below gist.

    注意 :我已遵循此编码标准,因此当需要显示特定单词的详细信息时,请在下面的要点中编写。

    Lets set this Publishable API key in your project file.

    让我们在项目文件中设置此可发布API密钥。

    @override
      void initState() {
        super.initState();
        StripeNative.setPublishableKey(publishableKey);
        StripeNative.setMerchantIdentifier(merchantIdentifier);
      }

    publishableKey : ‘ Your Stripe Account Publishable Key which mentioned on Dashboard ’

    publishableKey:'仪表板上提到的您的Stripe Account可发布密钥'

    merchantIdentifier : ‘ Test ’ (As we are in test mode)

    vendorIdentifier:“测试”(因为我们处于测试模式)

    Note : Please use the latest version of all plugins at the time of your development.

    注意:请在开发时使用所有插件的最新版本。

    Also, add below plugins (Ignore if already added).

    另外,添加以下插件(如果已添加,则忽略)。

    dependencies:
      flutter:
        sdk: flutter
    
    
      cupertino_icons: ^0.1.3
      stripe_native: ^1.2.3
      firebase_auth: ^0.16.1
      firebase_core: ^0.4.5
      cloud_functions: ^0.5.0
      fluttertoast: ^4.0.1
      intl: ^0.16.1

    We have two APIs for Charging the customers.

    我们有两个用于向客户收费的API。

    1. Charges API

      收费API

    2. Payment Intent API

      付款意图API

    Limitation of Charges API and Benefit of Payment Intent API :

    费用限制API和付款意向API:

    Charges API does not support scaling businesses or customers outside of the U.S. and Canada.

    Charges API不支持扩展美国和加拿大以外的业务或客户。

    Payment Intent API for more robust and global payments.

    Payment Intent API,用于更强大的全球支付。

    So After referring to the documentation of both APIs I have come up with this.

    因此,在参考了两个API的文档之后,我想到了这一点。

    Image for post

    It indicates to try new payments APIs and Integrations instead of old ones. So I am preferring you to use Payment Intent API instead of Charges API for charging the customers.

    它指示尝试使用新的付款API和集成而不是旧的。 因此,我希望您使用Payment Intent API而非Charges API向客户收费。

    So Let's do back-end part first using Node.js, Cloud Function, and Visual Studio (Only for Server Side Development).

    因此,让我们首先使用Node.js,Cloud Function和Visual Studio(仅用于服务器端开发)进行后端部分。

    在Firebase控制台上创建项目并获取Blaze计划 (Create Project On Firebase Console And Get Blaze plan)

    Image for post

    After Upgrade your plan to Blaze Plan

    将您的计划升级到Blaze计划后

    Image for post

    设置Firebase CLI以实施云功能 (Setup Firebase CLI To implement Cloud Function)

    Run the following commands in terminal :

    在终端中运行以下命令:

    npm install -g firebase-toolsfirebase init

    npm install -g firebase-tools firebase init

    After running the last command you will get this window in the terminal.

    运行最后一条命令后,您将在终端中看到此窗口。

    Image for post

    Choose Functions: Configure and Deploy Cloud Functions.

    选择功能:配置和部署云功能。

    Image for post

    After that please choose your project according to your project name and finish all the steps.Please install all the npm dependencies which it’s asking for. It generates some files into your functions folder.

    之后,请根据您的项目名称选择您的项目,并完成所有步骤。请安装所有要求的npm依赖项。 它将一些文件生成到您的functions文件夹中。

    Let's focus on the index.js file.

    让我们关注index.js文件。

    const functions = require('firebase-functions');
    const admin = require('firebase-admin');
    admin.initializeApp(functions.config().firebase);
    
    
    const firestore = admin.firestore();
    const settings = { timestampInSnapshots: true };
    firestore.settings(settings)
    const stripe = require('stripe')('<Your Secret Key>');
    
    
    //Create Payment
    exports.createPayment = functions.region('europe-west3').https.onCall(async (data, context) => {
      try {
        //Create Payment Method
        const paymentMethod = await stripe.paymentMethods.create(
          {
            type: 'card',
            card: {
              token: data.token,
            },
          },
        );
        // If You Want to Implement Combine Process Of CreatePayment and ConfirmPayment
        //then Please Uncomment this below two lines then return status and Do not Call Below ConfirmPayment
        //method From FrontEnd Side
    
    
        //Create PaymentIntent
        const paymentIntent = await stripe.paymentIntents.create({
          amount: data.amount,
          currency: data.currency,
          // payment_method: connectedAccountPaymentMethod.id,
          // confirm: true,
          description: 'Software development',
          shipping: {
            name: 'Jenny Rosen',
            address: {
              line1: '510 Townsend St',
              postal_code: '98140',
              city: 'San Francisco',
              state: 'CA',
              country: 'US',
            },
          },
        })
        return {
          paymentIntentId: paymentIntent.id,
          paymentMethodId: paymentMethod.id,
        }
      } catch (e) {
        console.log(e);
        return {
          error : e,
          paymentIntentId: "",
          paymentMethodId: ""
        }
      }
    });
    
    
    //For Confirmming Payment
    exports.confirmPayment = functions.region('europe-west3').https.onCall(async (data, context) => {
      try {
        const finalPayment = await stripe.paymentIntents.confirm(
          data.paymentIntentId,
          { payment_method: data.paymentMethodId });
        return {
          paymentStatus: finalPayment.status,
        }
      } catch (e) {
        console.log(e);
        return {
          error : e,
          paymentStatus : ""
        }
      }
    });
    
    
    //For Canceling Payment
    exports.cancelPayment = functions.region('europe-west3').https.onCall(async (data, context) => {
      try{
      const cancel = await stripe.paymentIntents.cancel(
        data.paymentIntentId,
      );
      return {
        cancelStatus: cancel.status,
      }
      }catch(e){
        console.log(e);
        return {
          error : e,
          cancelStatus : ""
        }
      }
    });

    First seven lines from the top used for getting access to platforms.

    从顶部开始的前七行用于访问平台。

    Please replace<Your Secret Key> into your secret key provided on Stripe Dashboard.

    请将<您的密钥>替换为Stripe仪表板上提供的密钥。

    exports.createPayment , exports.confirmPayment and exports.cancelPayment indicate our Cloud functions for Stripe Payment.

    Exports.createPayment,exports.confirmPaymentexports.cancelPayment指示我们用于条带支付的云功能。

    stripe.paymentMethods.create

    stripe.paymentMethods.create

    stripe.paymentIntents.create

    stripe.paymentIntents.create

    stripe.paymentIntents.confirm

    stripe.paymentIntents.confirm

    stripe.paymentIntents.cancel

    stripe.paymentIntents.cancel

    You can see these three lines in the above code. It's written from API documentation of Stripe to complete our payment integration from creating Payment Method to Confirm Payment and Cancel Payment.

    您可以在上面的代码中看到这三行。 它是从Stripe的API文档编写的,以完成从创建付款方式到确认付款和取消付款的付款集成。

    To deploy firebase cloud functions run the following command :

    要部署Firebase云功能,请运行以下命令:

    firebase deploy --only functions

    firebase deploy --only功能

    You can see cloud functions in your firebase console.

    您可以在Firebase控制台中查看云功能。

    Source 资源

    Now Let’s Call this cloud functions from our Front-end side. So open your Flutter Project in Android Studio.

    现在让我们从前端侧调用此云功能。 因此,在Android Studio中打开Flutter Project。

    Please note the below lines if you get any error when running.

    如果在运行时出现任何错误,请注意以下几行。

    Ensure to be on a minSdkVersion 19 in build.gradle(app-level) file.

    确保位于build.gradle(应用程序级别)文件中的minSdkVersion 19上。

    make sure to have multiDexEnabled true also.

    确保multiDexEnabled也为true

    defaultConfig {
            applicationId "com.jp.googlepaystripe"
            minSdkVersion 19
            targetSdkVersion 28
            versionCode flutterVersionCode.toInteger()
            versionName flutterVersionName
            multiDexEnabled true
        }

    First, make StripePaymentManager class to separate out code for calling API.

    首先,使StripePaymentManager类分离出用于调用API的代码。

    import 'package:cloud_functions/cloud_functions.dart';
    import 'package:flutter/material.dart';
    import 'package:googlepaystripe/home/model/cancel_payment_info.dart';
    import 'package:googlepaystripe/home/model/confirm_payment_info.dart';
    import 'package:googlepaystripe/home/model/payment_intent_info.dart';
    import 'package:googlepaystripe/utils/constants/constants.dart';
    import 'package:googlepaystripe/utils/progress_dialog.dart';
    
    
    class StripePaymentManager {
      HttpsCallableResult paymentIntentResult,
          confirmPaymentResult,
          cancelPaymentResult;
      HttpsCallable callPaymentIntent, callConfirmPayment, callCancelPayment;
      BuildContext context;
    
    
      PaymentIntentInfo __paymentInfoFromStripe(HttpsCallableResult intentInfo) {
        return intentInfo != null
            ? PaymentIntentInfo(
                paymentIntentId: intentInfo.data[paymentIntentIdKey],
                paymentMethodId: intentInfo.data[paymentMethodIdKey])
            : null;
      }
    
    
      ConfirmPaymentInfo __confirmPaymentInfoFromStripe(
          HttpsCallableResult confirmInfo) {
        return confirmInfo != null
            ? ConfirmPaymentInfo(paymentStatus: confirmInfo.data[paymentStatusKey])
            : null;
      }
    
    
      CancelPaymentInfo __cancelPaymentInfoFromStripe(
          HttpsCallableResult cancelInfo) {
        return cancelInfo != null
            ? CancelPaymentInfo(cancelStatus: cancelInfo.data[cancelStatusKey])
            : null;
      }
    
    
      //Call Create Payment Method and Create Payment Intent Stripe API
      Future startCreatePayment(BuildContext context, double totalAmount,
          String currencyName, String token) async {
        callPaymentIntent = CloudFunctions(region: europeRegion)
            .getHttpsCallable(functionName: createPaymentMethod);
        ProgressDialogUtils.showProgressDialog(context);
        try {
          paymentIntentResult = await callPaymentIntent.call(<String, dynamic>{
            amountKey: totalAmount,
            currencyKey: currencyName.toLowerCase(),
            tokenKey: token
          });
          ProgressDialogUtils.dismissProgressDialog();
          return __paymentInfoFromStripe(paymentIntentResult);
        } on CloudFunctionsException catch (e) {
          print(e.code);
          print(e.message);
          print(e.details);
        } catch (e) {
          print(e);
        }
      }
    
    
      //Call Confirm PaymentIntent Stripe API
      Future confirmPaymentIntent(
          BuildContext context, String intentId, String methodId) async {
        callConfirmPayment = CloudFunctions(region: europeRegion)
            .getHttpsCallable(functionName: confirmPaymentMethod);
        ProgressDialogUtils.showProgressDialog(context);
        try {
          confirmPaymentResult = await callConfirmPayment.call(<String, dynamic>{
            paymentIntentIdKey: intentId,
            paymentMethodIdKey: methodId,
          });
          ProgressDialogUtils.dismissProgressDialog();
          return __confirmPaymentInfoFromStripe(confirmPaymentResult);
        } on CloudFunctionsException catch (e) {
          print(e.code);
          print(e.message);
          print(e.details);
        } catch (e) {
          print(e);
        }
      }
    
    
      //Call Cancel PaymentIntent Stripe API
      Future cancelPayment(BuildContext context, String intentId) async {
        try {
          callCancelPayment = CloudFunctions(region: europeRegion)
              .getHttpsCallable(functionName: cancelPaymentMethod);
          ProgressDialogUtils.showProgressDialog(context);
          cancelPaymentResult = await callCancelPayment.call(<String, dynamic>{
            paymentIntentIdKey: intentId,
          });
          ProgressDialogUtils.dismissProgressDialog();
          return __cancelPaymentInfoFromStripe(cancelPaymentResult);
        } on CloudFunctionsException catch (e) {
          print(e.code);
          print(e.message);
          print(e.details);
        } catch (e) {
          print(e);
        }
      }
    }

    europeRegion : ‘ europe-west3 ’

    欧洲地区:“ europe-west3”

    createPaymentMethod : ‘ createPayment ’

    createPaymentMethod:'createPayment'

    confirmPaymentMethod : ‘ confirmPayment ’

    ConfirmPaymentMethod:“ confirmPayment”

    cancelPaymentMethod : ‘cancelPayment’

    cancelPaymentMethod:'cancelPayment'

    amountKey : ‘ amount ’

    amountKey:“金额”

    currencyKey : ‘ currency ’

    currencyKey:'货币'

    tokenKey : ‘ token ’

    tokenKey:“令牌”

    paymentIntentIdKey : ‘ paymentIntentId ’

    paymentIntentIdKey:“ paymentIntentId”

    paymentMethodIdKey : ‘ paymentMethodId ’

    paymentMethodIdKey:'paymentMethodId'

    paymentStatusKey : ‘ paymentStatus ’

    paymentStatusKey:“ paymentStatus”

    cancelStatusKey : ‘cancelStatus’

    cancelStatusKey:'cancelStatus'

    Three model classes : PaymentIntentInfo , ConfirmPaymentInfo and CancelPaymentInfo.

    三个模型类: PaymentIntentInfoConfirmPaymentInfoCancelPaymentInfo

    class PaymentIntentInfo {
      String paymentIntentId;
      String paymentMethodId;
      PaymentIntentInfo({this.paymentIntentId, this.paymentMethodId});
    }
    class ConfirmPaymentInfo {
      String paymentStatus;
      ConfirmPaymentInfo({this.paymentStatus});
    }
    class CancelPaymentInfo {
      String cancelStatus;
      CancelPaymentInfo({this.cancelStatus});
    }

    Okay, now you are done with the API calling part. So you have passed half of the implementation and will complete it soon.

    好的,现在您已经完成了API调用部分。 因此,您已经通过了一半的实施,并将很快完成。

    Source 资源

    Now let's go to GooglePayment Screen, declare these below variables which we will use in the future.

    现在,我们转到GooglePayment屏幕,声明以下这些变量,我们将在以后使用它们。

    class _GooglePaymentPageState extends State<GooglePaymentPage> {
      double totalAmount = 0;
      double amount = 0;
      String currencyName = "";
      Map receipt;
      Receipt finalReceipt;
      String generatedToken = "";
      final StripePaymentManager _stripePaymentManager = StripePaymentManager();

    Let’s build the Button Widget.

    让我们构建Button Widget。

    @override
      Widget build(BuildContext context) {
        return Scaffold(
            appBar: AppBar(
              backgroundColor: Colors.black,
              title: Text(
                googlePay,
                style: nativeTextStyle,
              ),
              centerTitle: true,
            ),
            body: Center(
              child: _buildNativeButton(),
            ));
      }
    
    
      //Native Button Widget
      Widget _buildNativeButton() {
        return Padding(
            padding: EdgeInsets.all(spacingSmall),
            child: RaisedButton(
                color: Colors.black,
                child: Text(
                  googlePay,
                  style: nativeTextStyle,
                ),
                onPressed: () {
                  _onNativeButtonClicked();
                }));
      }

    nativeTextStyle : You can define your prefer Textstyle.

    nativeTextStyle:您可以定义自己喜欢的Textstyle。

    spacingSmall : Any Size According to requirement.

    intervalSmall:根据要求任意大小。

    Image for post

    Now we want click event of this native button so let's go with the below code.

    现在,我们要单击此本机按钮的click事件,所以让我们看下面的代码。

    //Click Event Method For Google Pay Button To Start Create Payment
      _onNativeButtonClicked() async {
        currencyName = currency();
    //    Below Two lines is for Example To Show How To Pass Details To receiptPayment
    //    Method For Generating Token
        receipt = <String, double>{"Nice Hat": 5.00, "Used Hat": 1.50};
        finalReceipt = Receipt(receipt, "Hat Store");
        //You can pass Amount According To Your Payment
        amount = 0;
        receipt.values.forEach((element) {
          amount = amount + element;
        });
        totalAmount = amount * 100.0;
        generatedToken = await receiptPayment(finalReceipt);
        try {
          dynamic result = await _stripePaymentManager.startCreatePayment(
              context, totalAmount, currencyName, generatedToken);
          if (result is PaymentIntentInfo) {
            if (result != null) {
              confirmDialog(receipt, finalReceipt, result.paymentIntentId,
                  result.paymentMethodId);
            }
          }
        } catch (e) {
          ToastUtils.showToast(e, Colors.black, Colors.white);
          return e;
        }
      }
    
    
      //To Get Locale Based Currency Name
      String currency() {
        Locale locale = Localizations.localeOf(context);
        var format = NumberFormat.simpleCurrency(locale: locale.toString());
        return format.currencyName;
      }
    
    
      //To Generate Token (Method Already Implemented in Plugin)
      Future<String> receiptPayment(Receipt finalReceipt) async {
        return await StripeNative.useReceiptNativePay(finalReceipt);
      }

    Final Result After the click of Native Button.

    最终结果单击本机按钮后。

    Image for post

    You will see this error Unrecognized app. Please make sure you trust this app before proceeding, please don’t bother about it. This error indicates that the app uses ENVIRONMENT_TEST and doesn't receive real payment credentials. When you will change your environment from test mode to live mode error will be resolved.

    您将看到此错误无法识别的应用程序。 在进行下一步之前 ,请确保您信任此应用 ,请不要担心。 此错误表明该应用使用ENVIRONMENT_TEST ,并且未收到实际的付款凭证。 当您将环境从测试模式更改为实时模式时,将解决错误。

    According to me, we have to ask the user again that if he/she really wants to do further payment process. So I have added one AlertDialog to get confirmation from User. Please check the below gist for the Dialog feature.

    根据我的说法,我们必须再次询问用户,他/她是否真的要进行进一步的付款流程。 因此,我添加了一个AlertDialog来获得User的确认。 请检查以下要点,了解“对话”功能。

    //To Ask User To Confirm Payment Or Not
      confirmDialog(
          Map receipt, Receipt finalReceipt, String intentId, String methodId) {
        showDialog(
            context: context,
            barrierDismissible: false,
            builder: (BuildContext context) {
              return AlertDialog(
                  title: Text(confirmDialogTitle, style: headerTextStyle),
                  content: Container(
                      child:
                          Column(mainAxisSize: MainAxisSize.min, children: <Widget>[
                    Column(
                        children: finalReceipt.items.entries.map((e) {
                      totalAmount = totalAmount + e.value;
                      return Column(children: <Widget>[
                        Row(
                            mainAxisAlignment: MainAxisAlignment.spaceBetween,
                            children: <Widget>[
                              Text("${e.key}", style: titleTextStyle),
                              Text("${e.value}", style: titleContentTextStyle),
                            ])
                      ]);
                    }).toList()),
                    Row(
                        mainAxisAlignment: MainAxisAlignment.spaceBetween,
                        children: <Widget>[
                          Text("$payTo ${finalReceipt.merchantName}",
                              style: headerTextStyle),
                          Text(
                            "${finalReceipt.items.values.elementAt(0) + finalReceipt.items.values.elementAt(1)}",
                            style: titleContentTextStyle,
                          )
                        ])
                  ])),
                  actions: <Widget>[
                    RaisedButton(
                      child: Text(cancel, style: cancelTextStyle),
                      onPressed: () {
                        Navigator.of(context).pop();
                        cancelPayment(intentId);
                      },
                    ),
                    RaisedButton(
                        child: Text(confirm, style: confirmTextStyle),
                        onPressed: () {
                          Navigator.of(context).pop();
                          confirmPayment(intentId, methodId);
                        })
                  ]);
            });
      }

    Please define your TextStyle in every Textstyle which mentioned in above gist

    请在以上要点中提到的每个Textstyle中定义您的TextStyle

    When the user will go with the Continue button, there is some create payment process starting in the background and after this popup will be shown.

    当用户单击“继续”按钮时,将从后台开始执行一些创建付款过程,然后将显示此弹出窗口。

    Image for post

    Let’s implement the Confirm and Cancel Payment scenario.

    让我们实现“确认和取消付款”方案。

    //To Confirm the payment process
      confirmPayment(String intentId, String methodId) async {
        try {
          dynamic result = await _stripePaymentManager.confirmPaymentIntent(
              context, intentId, methodId);
          if (result is ConfirmPaymentInfo) {
            if (result != null) {
              if (result.paymentStatus == paymentSuccessStatus) {
                ToastUtils.showToast(
                    successPaymentStatus, Colors.black, Colors.white);
              }
            }
          }
        } catch (e) {
          ToastUtils.showToast(e, Colors.black, Colors.white);
          return e;
        }
      }
    
    
      //To cancel the payment process
      cancelPayment(String intentId) async {
        try {
          dynamic result =
              await _stripePaymentManager.cancelPayment(context, intentId);
          if (result is CancelPaymentInfo) {
            if (result != null) {
              if (result.cancelStatus == cancelStatus) {
                ToastUtils.showToast(
                    cancelPaymentStatus, Colors.black, Colors.white);
              }
            }
          }
        } catch (e) {
          ToastUtils.showToast(e, Colors.black, Colors.white);
          return e;
        }
      }

    paymentSuccessStatus : ‘ succeeded ’

    paymentSuccessStatus:“成功”

    cancelStatus : ‘ canceled ’

    cancelStatus:“已取消”

    successPaymentStatus : ‘ Payment Successful ’

    successPaymentStatus:'付款成功'

    cancelPaymentStatus : ‘ Payment Canceled ’

    cancelPaymentStatus:“付款已取消”

    Okay, finally you have done a great job so far. Please run the following command in the terminal.

    好吧,到目前为止,您到目前为止做得很好。 请在终端中运行以下命令。

    flutter run

    扑动

    When a user taps on the Confirm button, the final result is…

    当用户点击“确认”按钮时,最终结果是…

    Image for post

    Yeah, we did it. You can see this in your Stripe Dashboard also.

    是的,我们做到了。 您也可以在“条纹仪表板”中看到它。

    Image for post

    When a user taps on the Cancel button, the final result is…

    当用户点击“取消”按钮时,最终结果是…

    Image for post

    Yeah, we did it. You can see this in your Stripe Dashboard also.

    是的,我们做到了。 您也可以在“条纹仪表板”中看到它。

    Image for post
    Source 资源

    结论 (Conclusion)

    We have done all the Implementation and Google Pay Payments using the Stripe testing section. If you want to do implement for Production purpose then you have to Activate your account by filling your business details. So After that, you can get access to Live API keys and go ahead.

    我们已经使用条纹测试部分完成了所有实施和Google Pay付款。 如果您要为生产目的而实施工具,则必须通过填写业务详细信息来激活您的帐户。 因此,在那之后,您可以访问Live API密钥并继续。

    So, that’s it for Google Pay using Stripe in Flutter.

    因此, 使用Flutter中的Stripe的Google Pay就是这样。

    You can find the whole project with clean architecture on Git Repository :

    您可以在Git Repository上找到具有干净架构的整个项目:

    翻译自: https://medium.com/swlh/accept-payments-using-google-pay-through-stripe-in-flutter-d65dfaa0091a

    谷歌flutter

    展开全文
  • Polycarpus is sure that his life fits the description: "first there is a white stripe, then a black one, then a white one again". So, Polycarpus is sure that this rule is going to fulfill during the ...
  • White, Black and White Again time limit per test 3 seconds memory limit per test 256 megabytes input standard input output standard output Polycarpus is sure that his
  • days if Polycarpus is in for a white stripe (a stripe that has good events only, the stripe's length is at least 1 day), the a black stripe (a stripe that has not-so-good events only, the stripe's ...
  • 点燃烧瓶 Ignite是用于启动新Flask应用程序的脚手架。... Ignite建立在现代Flask应用程序的最佳实践之上。... 一次性使用信用卡和收据购买(使用Stripe) Heroku / Docker部署 :white_heavy_check_mark: 某些平
  • You are given a rectangle 1*N, where its ...1*1 squares can be painted as white or black. So, one can build a "code" of this rectangle - this will be a sequence of numbers, the number of consequent bla
  • Adidas famous three stripe football shirt name and number lettering. It remains to be seen which will be the ool?style in 2010. What makes the Air Nike so special? Yes, it is in the name of a legend...
  • 1、stripe:是否显示斑马纹 <el-table :stripe="true"> </el-table> // 后来发现可以直接这么写,哎之前好傻 <el-table stripe> </el-table> 2、可以自己更改行颜色,样式如下 .el-...
  • if data.shape[2]==1: #in case it is black and white data = np.reshape(data,(data.shape[0],data.shape[1])) if np.max(data)>1: img = Image.fromarray(data.astype(np.uint8)) #the image is already 0-...
  • A. Vasya and Petya's Game(cf第一题)

    千次阅读 2015-09-11 18:49:20
    题目链接 Vasya and Petya are playing a simple game. Vasya thought of number x between 1 and n, and Petya tries to guess the number. Petya can ask questions like: "Is the unknown number ...
  • } .main{ background-color: white; } style> 10、编写user相关代码(前端核心逻辑代码、增删改查还有模糊查询,包括逻辑删除和物理删除) 在views目录下创建user目录,在home目录下创建index.vue和edit.vue 编写...
  • ,"I bought this dress in the navy stripe version and love it. the material is so soft it's like wearing pajamas, and the actual swing skirt is flattering (which surprised me when i tried it on). i ...
  • Staart API是用于以TypeScript编写的SaaS启动的Node... :white_medium_star: 产品特点 :NEW_button: v2的新功能 Casbin支持的权限管理 JWT提供的一次性优惠券代码 Redis支持的出站电子邮件和日志队列 与云无关,不再特
  • 你以为 CSS 只是个简单的布局?

    千次阅读 2018-05-19 08:34:49
    color: white; font-size: 20px; } .bubbly:after { content: ''; position: absolute; bottom: 0; left: 50%; border: 34px solid transparent; border-top-color: #00ccbb; border-bottom: 0; border-...
  • pycharm 皮肤主题及个性化设置

    千次阅读 2018-04-01 12:39:56
    -> 勾选“Show line numbers”、“Show whitespaces”、“Show method separators” 关闭PEP8.py语言风格提示警告信息 在PyCharm项目设置中找到inspections选项,然后找到PEP 8 coding style violation,...
  • https://aws.amazon.com/whitepapers/ 各种云服务的常见问题: https://aws.amazon.com/faqs/ AWS各种服务的介绍: http://aws.amazon.com/cn/documentation/ •模考Practice Exam 考一次20刀。 强烈建议花...
  • GlusterFS卷类型及数据分布分析

    千次阅读 2018-04-04 14:45:52
    Glusterfs支持七种Volume,即Distribute卷、Stripe卷、Replica卷、Distribute stripe卷和Distribute replica卷,Distribute Stripe, Replica Volume卷这七种卷可以满足不同应用对高性能、高可用的需求。基本卷:(1)...
  • KVM 操作虚拟机常用命令

    万次阅读 2017-07-10 08:50:59
    blocks, Stripe width= 0 blocks 3276800 inodes, 13107142 blocks 655357 blocks ( 5.00 %) reserved for the super user First data block= 0 Maximum filesystem blocks= 4294967296 400 block ...
  • Oracle 12cR1 RAC 在VMware Workstation上安装(上)—OS环境配置       1.1  整体规划部分 ...在同一个集群中,可以支持具有速度和规模不同的机器,但所有节点必须运行在相同的操作系统。...
  • Noise, being a pseudorandom artist

    千次阅读 2015-08-06 17:15:01
    原文:... In this tutorial you will create your own texture, and make it interesting with pseudorandom noise.You'll learn to Create and fill a texture;Visu
  • GlusterFS集群文件系统概述

    千次阅读 2014-10-25 23:07:28
    多个存储服务器可以通过客户端或存储网关上的卷管理器组成集群,如Stripe(RAID0)、Replicate(RAID1)和DHT(分布式Hash)存储集群,也可利用嵌套组合构成更加复杂的集群,如RAID10。 由于没有了元数据服务器...
  • 老外写的系统统计脚本

    千次阅读 2015-03-12 23:02:55
    [root@mysqlmaster mysql]# bash system_summary  # Aspersa System Summary Report ##############################  Date | 2015-03-12 07:55:19 UTC (local TZ: CST +0800)  Hostname | mysqlmaster
  • Lustre I/O性能特点与最佳实践

    万次阅读 热门讨论 2011-05-30 22:36:00
    Lustre系统中,每个文件对应MDT上的一个元数据文件,inode以扩展属性记录了数据分片布局信息,包括stripe_count(对象数), stripe_size(分片大小), stripe_offset(起始OST)以及每个OST对象信息。当客户数据端...
  • Flag (SDUT 2060)

    千次阅读 2014-07-01 09:59:33
     Flag Time Limit: 1000MS Memory limit: 65536K ...On the Day of the Flag of Russia a shop-owner decided to decorate the show-window of his shop with textile stripes of white, blue and
  • GlusterFS集群文件系统研究

    万次阅读 热门讨论 2011-03-28 21:01:00
    Stripe ( RAID0 )、 Replicate ( RAID1 )和 DHT (分布式 Hash )存储集群,也可利用嵌套组合构成更加复杂的集群,如 RAID10 。 由于没有了元数据服务器,客户端承担了更多的功能,包括数据卷管理、 I/O 调度...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 875
精华内容 350
关键字:

stripewhite