triniwiz / nativescript-stripe

Apache License 2.0
49 stars 34 forks source link

Using CreditCardView To Create a Stripe Token (NS-Vue) #86

Open AngeloAnolin opened 4 years ago

AngeloAnolin commented 4 years ago

Looking for some help here with regards to implementing the CreditCardView to get the user's credit card information and proceed to creating a Stripe Token that can be passed on the backend service.

My code is below:

<template>
  <Page>
    <StackLayout>
      <CreditCardView id="creditCard" ref="creditCard"></CreditCardView>
        <Button 
            horizontalAlignment="center"
            text="Save Payment Info" 
            @tap="savePaymentInfo()" />
    </StackLayout>
  </Page>
</template>

<script>
  import { Card, Stripe, CreditCardView } from 'nativescript-stripe'

  export default {
    data () {
      return {}
    },

    methods: {
      savePaymentInfo () {
        // How Do I Get Reference to the <CreditCardView></CreditCardView> markup here?
        let card = this.$refs.creditCard.card // NOT WORKING!!!
        const stripe = new Stripe('PUBLISHABLE_KEY')
        stripe.createToken(card).then((token) => {
          // Do something with the Token here..
        }, (error) => {
          // Do something with the Error here.
        }
      }
    }

  }
</script>

I am quite unsure what would the next step would be to obtain the token which I can use further to process for payment.

Appreciate any insight / help on this.

Thanks.

RobertGardner commented 4 years ago

I'm not familiar with NS-Vue, but asharghi submitted a Pull Request with an initial implementation of a Vue demo app for the Standard Integration. You might be able to use this to figure out how to get CreditCardView working for you.

AngeloAnolin commented 4 years ago

Is there additional references on how the CreditCardView works further? Like events or anything that is being triggered when a card has been entered?

I will look at the PR made by @ashargi and see whether that could work for my use case.

RobertGardner commented 4 years ago

I suggest you look at either demo or demo-angular and the code in, for example, demo/app/demo/ccview-page.[ts|xml] for an example of how to use CreditCardView in NS JavaScript.

Note: CreditCardView simply extends from View and provides a card field. (And a createNativeView() method for getting a platform-specific View, which hopefully you won't need.)

AngeloAnolin commented 4 years ago

Hi @RobertGardner

I tried to do a simple programmatic check to see whether I can get things working:

import { Card, Stripe } from 'nativescript-stripe'

generateToken() {
  let card = new Card('4242424242424242', 11, 2022, '295')
  let stripe = new Stripe('PUBLISHABLE_KEY')
  stripe.createToken(card).then((token) => {   
    // Use this token and pass it to server side for further processing.
  }, (error) => {
    // Handle error here.
  }
}

Doing the following:

console.log('card', JSON.stringify(card))
console.log('stripe', JSON.stringify(stripe))

yields:

'card' '{"native":{}}'
'stripe' '{}'

So it seems I am missing some implementation here.

Thanks.

Sauvetonbio commented 4 years ago

Hello, this is how we made it work, by creating a payment method :

import { Stripe, Card } from 'nativescript-stripe';
let stripeReady = new Stripe('PUBLISHABLE_KEY');

let cc = new Card('4242424242424242', 11, 2022, '295');
let myCallback = function getPaymentMethod(err, pm){
    if(pm){
        console.log(pm.id);
    }else if(err){
        console.log(err);
    }
}
stripeReady.createPaymentMethod(cc, myCallback);
drangelod commented 4 years ago

I was running into similar issues getting everything working. You can see the complete solution that worked with NS Vue here: https://blog.angelengineering.com/shopping-app/

sebj54 commented 3 years ago

I had trouble figuring how to make this plugin work with Vue too. So here is a complete solution (thanks @drangelod and @Sauvetonbio, you helped me a lot!):

<template>
    <stack-layout>
        <credit-card-view ref="cardView" />

        <button
            :text="'cards.addCardSubmit'|L"
            class="-primary"
            @tap="addCard"
        />
    </stack-layout>
</template>

<script>
import { Stripe, Card } from 'nativescript-stripe'

export default {
    data() {
        return {
            intentClientSecret: null,
            stripe: null,
        }
    },
    created() {
        // Create a payment intent (or a setup intent) on your server and fetch it here
        // Set intentClientSecret with the intent's client_secret property
    },
    methods: {
        addCard() {
            const cardView = this.$refs.cardView.nativeView
            let card = null

            if (cardView.android) {
                const androidCard = cardView.android.getCard()

                // Had to check this because it is null if card is invalid
                if (androidCard) {
                    card = new Card(
                        androidCard.getNumber().toString(),
                        Number(androidCard.getExpMonth()), // cast to Number if mandatory to avoid invalid card
                        Number(androidCard.getExpYear()), // cast to Number if mandatory to avoid invalid card
                        androidCard.getCvc().toString()
                    )
                }
            } else if (cardView.ios) {
                card = new Card(
                    cardView.ios.cardNumber.toString(),
                    Number(cardView.ios.expirationMonth),
                    Number(cardView.ios.expirationYear),
                    cardView.ios.cvc.toString()
                )
            }

            if (card && card.validateCard()) {
                this.stripe.createPaymentMethod(card, (error, paymentMethod) => {
                    if (error) {
                        this.displayError(error)
                    } else {
                        this.stripe.confirmSetupIntent(paymentMethod.id, this.clientSecret, (setupError, setupIntent) => {
                            if (error) {
                                this.displayError(error)
                            } else {
                                this.displaySuccess()
                            }
                        })
                    }
                })
            } else {
                this.displayError(new Error('Invalid card'))
            }
        },
        displayError(error) {
            // implement your error logic here
        },
        displaySuccess() {
            // implement your error logic here
        },
    },
}
</script>
mreall commented 1 year ago

@sebj54, is the code still working for you? I'm able to get the view from this.$refs.cardView.nativeView, but this.$refs.cardView.nativeView.android.getCard() throws the exception: TypeError: this.$refs.cardView.nativeView.android.getCard is not a function.

this.$refs.cardView.nativeView.android.getCardParams() returns an object with the card data I entered, but I can't access the data in the object (i.e. object.number is undefined and object.getNumber() throws an exception). Do you have any suggestions? I'm running on an Android emulator (I haven't tried the iOS code yet).

sebj54 commented 1 year ago

@mreall No it doesn't work anymore with latest version. Here is what you have to do instead:

addCard() {
    const cardView = this.$refs.cardView.nativeView

    if (cardView.cardParams) {
        this.stripe.createPaymentMethod(cardView.cardParams, (error, paymentMethod) => {
            // ...
        })
    } else {
        this.displayError(getErrorFromCode(STRIPE_INVALID_CARD))
    }
}
mreall commented 1 year ago

Thanks @sebj54. Is there a way to access the card details that were entered, such as the card number and address? I don't actually use stripe, but use the plugin for its user interface for entering card information.

sebj54 commented 1 year ago

If I remember well, cardView.cardParams is a Card object, the same you had with the old method.

Also, look at the README if you'd like to use the latest version of the plugin, because this one is outdated.

mreall commented 1 year ago

@sebj54, thanks for the feedback. cardView.cardParams doesn't provide access to the card parameters on Android and the README is outdated. Those were the first places I looked and I spent hours trying to figure out how to work around it.

By converting the CardParams object to a string, I can extract the card details. It's a hack, but it's the only way I could get it to work on Android. Here's what I'm doing.

    addCard() {
      const ccView = this.$refs.ccView.nativeView;
      let card = null;
      if (ccView.android) {
        card = ccView.android.getCardParams();
        const cardParts = card.toString().split(',');
        const cardDetails = {};
        cardParts.forEach(item => {
          const split = item.split('=');
          cardDetails[split[0].trim()] = split[1].trim();
        });
        if (card) {
          this.card.number = cardDetails.number;
          this.card.expMonth = cardDetails.expMonth;
          this.card.expYear = cardDetails.expYear;
        }
      } else if (ccView.ios) {
        if (ccView.ios.cardNumber) {
          this.card.number = ccView.ios.cardNumber;
          this.card.expMonth = '' + ccView.ios.expirationMonth;
          this.card.expYear = '20' + ccView.ios.expirationYear;
        }
      }
    },