I am facing a challenge in integrating STRIPE Payment Request Button in my PWA using Polymer 2.0. I am not able to add the stripe payment button to the div inside template tag of the element.
I have a stripe-payment element created. It's basically a simple polymer element with template dom-bind and basic div tag inside a card.
What I wish to do is add the stripe payment request button to the div id = hostcard. At present the stripe payment request button is displayed on the top of the page in full width 100%.
Inserted code in comments in the element code below on what I tried to date.
Got 2 questions:
1. how can I add the button to the div hostcard so that it will be displayed inside the card?
2. is there a better way to show the payment request button from polymer element?
stripe-payment.html
<script src="https://js.stripe.com/v3/"></script>
<dom-module id="stripe-payment">
<template is="dom-bind">
<style include="shared-styles">
</style>
<template is="dom-if" if="[[signedin]]">
<div id="hostcard" class="card">
<div class="circle">3</div>
<h1>Stripe Payment</h1>
</div>
</template>
</template>
<script>
class StripePayment extends Polymer.Element {
static get is() { return 'stripe-payment'; }
static get properties() {
return {
user: { type: Object, notify: true, readOnly: false, observer: '_userChanged' },
};
}
ready() {
super.ready();
}
}
//STRIPE CODE
const DIVx = document.createElement('div');
const buttonSlot = DIVx.cloneNode();
buttonSlot.setAttribute('slot', 'button');
// Create stripe card wrapper
let button = DIVx.cloneNode();
button.id = 'payment-request-button';
// Add wrapper to slot
buttonSlot.appendChild(button);
//None worked -- either give null or undefined or function not defined
//this.$.hostcard.appendChild(buttonSlot);
//this.$$('#hostcard').appendChild(buttonSlot);
//var myElement = document.getElementById('stripe-payment'); - not working
//myElement.$.hostcard.appendChild(buttonSlot);
var element = this.shadowRoot;
console.log('Element is ->'+element);
document.body.appendChild(buttonSlot);
// Create a Stripe client
var stripe = Stripe('pk_test_**************');
// Create an instance of Elements
var elements = stripe.elements();
// Create a payment charges
var paymentRequest = stripe.paymentRequest({
country: 'AU',
currency: 'aud',
total: {
label: 'BizRec - Subscription',
amount: 100, //in cents
},
});
// Create an instance of Payment Request Button
var prButton = elements.create('paymentRequestButton', {
paymentRequest: paymentRequest,
style: {
paymentRequestButton: {
type: 'default' , // | 'donate' | 'buy' | default: 'default'
theme: 'dark', // | 'light' | 'light-outline' | default: 'dark'
height: '40px', // default: '40px', the width is always '100%'
},
},
});
// Check the availability of the Payment Request API first.
paymentRequest.canMakePayment().then(function(result) {
if (result) {
prButton.mount('#payment-request-button');
} else {
document.getElementById('payment-request-button').style.display = 'none';
// Add an instance of the card Element into the `card-element` <div>
//cardElement.mount('#card-element');
}
});
paymentRequest.on('token', function(ev) {
// Send the token to your server to charge it!
fetch('/charges', {
method: 'POST',
body: JSON.stringify({token: ev.token.id}),
})
.then(function(response) {
if (response.ok) {
// Report to the browser that the payment was successful, prompting
// it to close the browser payment interface.
ev.complete('success');
} else {
// Report to the browser that the payment failed, prompting it to
// re-show the payment interface, or show an error message and close
// the payment interface.
ev.complete('fail');
}
});
});
window.customElements.define(StripePayment.is, StripePayment);
</script>
</dom-module>
At the moment, Elements does not support the Shadow DOM as you mentioned. If you use Polymer then you likely need to do some custom work on your end. You can see this project by a third-party developer which should help unblock things: https://github.com/bennypowers/stripe-elements
Related
I am trying to implement Stripe payments in my Next.js app as described in the guide here: https://stripe.com/docs/payments/quickstart
The guide tells me that in order to use Stripe Elements for my checkout form, I need to know payment intent. It says:
Create PaymentIntent as soon as the page loads
The issue is - our website will not have a separate payments page, the payment form will be displayed inside the modal, which is loaded on every page of the website. That means, I would have to fetch the payment intent for any user who ever visits any page on our website, whether they're planning to purchase the course or not, just so that I could display the payment form inside the modal. That doesn't seem right to me.
Can you give me some advice, let me know if there's a better way to handle this?
Another issue is that this guide tells me that I should pass the fetched payment intent clientSecret as an option to <Elements/> wrapper.
And if I hover on <Elements/> wrapper in my VSCdoe, it tells me:
[...] Render an Elements provider at the root of your React app so that it is available everywhere you need it. [...]
So, does that mean I have to put <Elements/> wrapper into my _app.tsx file? And that means I'd have to fetch the payment intent clientSecret inside of the _app.tsx? So that my app would fetch payment intent secret any time any user ever loads any page on my website?
Again, this seems pretty weird, wouldn't it slow things down, add extra requests and loading time to all my pages, and create a whole bunch of payment intents that are never used?
Render the payment form in a modal in Layout.js and wrap the
entire project in the Layout component
place this code in _app.js
import React, { useEffect, useState } from "react"
import { loadStripe } from "#stripe/stripe-js"
import { Elements } from "#stripe/react-stripe-js"
import Layout from "../components/Layout"
import PaymentModalForm from "../components/PaymentModalForm"
const promise = loadStripe("pk_test_....")
// replace pk_test_... with your publishable key
const API_URL = "http://localhost:8000"
// replace API_URL with your backend server url
const App = ({ Component, pageProps }) => {
const [secret, setSecret] = useState(null)
useEffect(() => {
const fetchSecret = async () => {
const response = await fetch(`${API_URL}/create_intent`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
items: [{ id: 'adidas boost', quantity: 2}]
})
})
const { client_secret } = await response.json()
setSecret(clientSecret)
}
fetchSecret()
}, [])
const options = {
clientSecret: secret,
appearance: { theme: "stripe"}
}
return (
{secret && (
<Elements stripe={promise} options={options}>
<Layout>
<Component {...pageProps} />
</Layout>
</Elements>
)}
)
}
export default App
Then in your Layout.js, fill in this code
import PaymentModalForm from "../components/PaymentModalForm"
import React, { useEffect, useState } from "react"
const Layout = ({ children }) => {
const [showModal, setShowModal] = useState(false)
const handleClick = () => {
if (showModal) {
setShowModal(false)
} else {
setShowModal(true)
}
}
return (
<div>
<div className="container">
{children}
<button onClick={handleClick}>Show Payment Modal</button>
</div>
{showModal ? (
<div className="modal fade">
<div className="modal-dialog">
<div className="modal-content">
<PaymentModalForm />
</div>
</div>
</div>
) : ( null )}
</div>
)
}
export default Layout
There's more work to be done in PaymentModalForm.js
I simply don't know JS well enough to determine the issue. I'm sure it's glaring at me, in plain sight. I just need the button to function like a button. When I move the cursor over the button and click, neither do anything.
I've tried adding the type, as you can see.
<!-- Load Stripe.js on your website. -->
<script src="https://js.stripe.com/v3"></script>
<!-- Create a button that your customers click to complete their purchase. Customize the styling to suit your branding. -->
<button
style="background-color:#6772E5;color:#FFF;padding:8px 12px;border:0;border-radius:4px;font-size:1em"
id="checkout-button-plan_555xxx555"
role="link"
type="button"
>
Checkout
</button>
<div id="error-message"></div>
<script>
(function() {
var stripe = Stripe('pk_test_555xxx555');
var checkoutButton = document.getElementById('checkout-button-plan_G2Z8GjQU8ZihZw');
checkoutButton.addEventListener('click', function () {
// When the customer clicks on the button, redirect
// them to Checkout.
stripe.redirectToCheckout({
items: [{plan: '555xxx555', quantity: 1}],
// Do not rely on the redirect to the successUrl for fulfilling
// purchases, customers may not always reach the success_url after
// a successful payment.
// Instead use one of the strategies described in
// https://stripe.com/docs/payments/checkout/fulfillment
successUrl: window.location.protocol + '//cozelosdata.com/success',
cancelUrl: window.location.protocol + '//cozelosdata.com/canceled',
})
.then(function (result) {
if (result.error) {
// If `redirectToCheckout` fails due to a browser or network
// error, display the localized error message to your customer.
var displayError = document.getElementById('error-message');
displayError.textContent = result.error.message;
}
});
});
})();
</script>
Your id for the button is wrong.
<button id="checkout-button-plan_G2Z8GjQU8ZihZw" role="link" type="button">Checkout</button>
I have a list of items where the title is a link to display a detailed view of the item. Click the title and it correctly goes to url + Id. In the Vue tolls the detail page retrieves the item with matching ID but as and array not an object and the template does not display any properties - what am I missing?
<script>
import axios from "axios";
export default {
name: "Report",
data() {
return {
report: {}
};
},
mounted: function() {
this.getReport();
},
methods: {
getReport() {
let uri = "http://localhost:5000/api/reports/" + this.$route.params.id;
axios.get(uri).then(response => {
this.report = response.data;
});
}
}
};
</script>
The template is so
<template>
<v-content>
<h1>report detail page</h1>
<p>content will go here</p>-
<h3>{{ report.month }}</h3>
<pre>{{ report._id }}</pre>
</v-content>
</template>
any comments appreciated
url + Id
It sounds like your issue is that you are receiving an array not an object.
You can pull out objects encapsulated inside arrays easily.
For example, if we had the following data:
var bus1 = {passengers:10, shift:1}
var busArr = [bus1]
which we can assert: busArr === [{passengers:10, shift:1}]
We could then pull out bus1 by referencing the index 0:
var bus1New = busArr[0]
If you want to avoid the data transformation and just output the structure you can consider a v-for in your template.
<p v-for="val in report">
_id: {{val._id}}
<br>
month: {{val.month}}
</p>
so I am trying to integrate stripe checkout button (beta version that get's you to a Stripe checkout page) the problem is that the button is gray and plain and wasn't able to change size or color or format it in general.
I don't have any coding skills, I have tried to use a CSS custom button but not sure how can I link this to the original code.
So I appreciate if anyone could help me to either:
Have the ability to link the outcome of clicking on the stripe original button to one of my buttons OR
Help me format the button, by increasing it's size, using a specific font and choosing a color so that I can modify them myself.
Thanks a lot in advance
This is the code I am using:
<!-- Load Stripe.js on your website. -->
<script src="https://js.stripe.com/v3"></script>
<!-- Create a button that your customers click to complete their purchase. -->
<button id="checkout-button">StandardBlue</button>
<div id="error-message"></div>
<script>
var stripe = Stripe('KEYYYYYYYYYYYYYYYYYYYYYYYYYYYY', {
betas: ['checkout_beta_3']
});
var checkoutButton = document.getElementById('checkout-button');
checkoutButton.addEventListener('click', function () {
// When the customer clicks on the button, redirect
// them to Checkout.
stripe.redirectToCheckout({
items: [{sku: 'SKU', quantity: 1}],
successUrl: 'https://WEBSITE.COM/success',
cancelUrl: 'https://WEBSITE.COM/canceled',
})
.then(function (result) {
if (result.error) {
// If `redirectToCheckout` fails due to a browser or network
// error, display the localized error message to your customer.
var displayError = document.getElementById('error-message');
displayError.textContent = result.error.message;
}
});
});
</script>
Pretty simple here. Just add css with the selector #checkout-button.
Here is an example: https://jsfiddle.net/tonyprovenzola/sb7dtakz/3/
button#checkout-button {
font-family:'Arial', sans-serif;
padding:10px 14px;
background-color:#255eba;
border-radius:10px;
color:#fff;
cursor:pointer;
border:none;
outline:none;
}
button#checkout-button:hover {
background-color:#112a51;
}
I am trying to integrate Stripe "Pay with Card" checkout into backbone Node environment. On the server side, I am using Stripe Node code - that part works good. However, on the client side, I am unable to capture the event.
I would like to capture the submit event from the Stripe popup to call "paymentcharge" method in the view.
Here is my code:
<!-- Stripe Payments Form Template -->
<form id="stripepaymentform" class="paymentformclass">
<script
src="https://checkout.stripe.com/v2/checkout.js" class="stripe-button"
data-key="pk_test_xxxxxxxxxxxxx"
data-amount="0299"
data-name="MyDemo"
data-description="charge for something"
data-image="assets\ico\icon-72.png">
</script>
</form>
Backbone View Class
myprog.PaymentPanelView = Backbone.View.extend({
initialize: function () {
this.render();
},
render: function () {
$(this.el).html(this.template());
return this;
},
events : {
"submit" : "paymentcharge"
},
paymentcharge : function( event) {
this.model.set({stripeToken: stripeToken});
}
});
Backbone Model Class
var PaymentChargeModel = Backbone.Model.extend({
url: function(){
return '/api/paymentcharge';
},
defaults: {
}
})
Setup/Call the View from header menu event
if (!this.paymentPanelView) {
this.paymentPanelView = new PaymentPanelView({model: new PaymentChargeModel()});
}
$('#content').html(this.paymentPanelView.el);
this.paymentPanelView.delegateEvents();
this.selectMenuItem('payment-menu');
I think the problem has to do with your View's el and the event you are listening for.
You never explicitly define your View's el, which means it gets initialized to a detached <div> element. You then use your template to fill that <div> with the form element from the template. Even though your <div> is detached, you get to see the content, because you add the content of you el to #content using jquery.
I think the problem is that you are listening for a submit event on the <div> in your el, not the contained <form>. Try changing your events hash to this:
events: {
'submit form#stripepaymentform': 'paymentcharge'
}
Basically, listen for events on the contained element like in jquery's .on. You can also go right to a button click, something like this:
'click #mysubmitbutton': 'paymentcharge'
Hope this helps!