i am using stripe on my ruby on rails 5 website for my payment gateway plans. I am using the api in the client-side just as it appears in this link. I need to add a coupon to my plan, I could create it in the stripe dashboard in the testmode like the plan but adding the id to the code doesn't redirect me to the stripe checkout page. this is my javascript code:
<script src="https://js.stripe.com/v3"></script>
<script type="text/javascript">
$(document).ready(function() {
var stripe = Stripe('<%= Rails.configuration.stripe[:publishable_key] %>');
$('.payment-action').on('click', function() {
const data = $(this).data();
const user_email = '<%= current_user ? current_user.email : ""%>'
const user = '<%= current_user.id%>'
stripe.redirectToCheckout({
lineItems: [{
// Define the product and price in the Dashboard first, and use the price
// ID in your client-side code. You may also pass a SKU id into the `price`
// field
price: data['plankey'],
quantity: 1
}],
customerEmail: user_email,
mode: 'subscription',
subscriptionData: {
coupon: 'WaS5wFHC'
},
successUrl: 'https://www.my_web.network/success_payment?session_id={CHECKOUT_SESSION_ID}&p_i='+data['plan']+'&us='+user,
cancelUrl: 'https://www.my_web.network/update_plan'
});
});
});
</script>
I've been trying to get it to appear on this page using subscription_data or subscriptionData but it still doesn't work, what could I be missing?
<script type="text/javascript">
$(document).ready(function() {
var stripe = Stripe('<%= Rails.configuration.stripe[:publishable_key] %>');
$('.payment-action').on('click', function() {
const data = $(this).data();
const user_email = '<%= current_user ? current_user.email : ""%>'
const user = '<%= current_user.id%>'
stripe.redirectToCheckout({
lineItems: [{
// Define the product and price in the Dashboard first, and use the price
// ID in your client-side code. You may also pass a SKU id into the `price`
// field
price: data['plankey'],
quantity: 1
}],
customerEmail: user_email,
mode: 'subscription',
subscription_data: {
coupon: 'WaS5wFHC'
},
successUrl: 'https://www.my_web.network/success_payment?session_id={CHECKOUT_SESSION_ID}&p_i='+data['plan']+'&us='+user,
cancelUrl: 'https://www.my_web.network/update_plan'
});
});
});
</script>
It's not possible to use coupons for subscriptions in client-side only Checkout. You'd have to create a Checkout Session on your server where you pass in your coupon ID: https://stripe.com/docs/api/checkout/sessions/create#create_checkout_session-subscription_data-coupon
Related
I am trying to create a customer and create a subscription.
But when I do it I get error.
I tried doing it the node.js way and the php way. via node.js
I have installed stripe and via php I've installed composer and stripe/stripe-php.
When I do it the javascript way everything goes find and handy with no error out put but when I go to the stripe daashboard there is no new creation of a customer or subscription in the test mode data.
<?php // Create a customer using a Stripe token
// If you're using Composer, use Composer's autoload:
require_once('vendor/autoload.php');
// Be sure to replace this with your actual test API key
// (switch to the live key later)
\Stripe\Stripe::setApiKey("pk_test_pjUIBAynO2WA1gA7woSLBny3");
try
{
$customer = \Stripe\Customer::create([
'email' => $_POST['stripeEmail'],
'source' => $_POST['stripeToken'],
]);
$subscription = \Stripe\Subscription::create([
'customer' => $customer->id,
'items' => [['plan' => 'gold']],
]);
if ($subscription->status != 'incomplete')
{
header('Location: thankyou.html');
}
else
{
header('Location: payment_failed.html');
error_log("failed to collect initial payment for subscription");
}
exit;
}
catch(Exception $e)
{
header('Location:oops.html');
error_log("unable to sign up customer:" . $_POST['stripeEmail'].
", error:" . $e->getMessage());
}
and this is the html file
<form action="/create_subscription.php" id="start" method="POST">
<script
src="https://checkout.stripe.com/checkout.js" class="stripe-button"
data-key="pk_test_pjUIBAynO2WA1gA7woSLBny3"
data-name="500 a month package"
data-description="Subscription will be billed every 30 days"
data-amount="50000"
data-label="Get Started!">
</script>
<script>
var stripe = require("stripe")("pk_test_pjUIBAynO2WA1gA7woSLBny3");
var stripeToken = req.body.stripeToken;
stripe.subscriptions.create({
customer: id,
items: [
{
plan: "prod_EtGDP9qICl6tvW",
interval: "month"
},
]
} , function(err, subscription) {
// asynchronously called
function(err, custumor){
if(err){
res.send({
success: false,
message:'Error'
});
} else {
const { id } = customer;
}
};
stripe.customers.create({
email:"jenny#rosen.com",
source: stripeToken,
},
function(err, custumor){
if(err){
res.send({
success: false,
message:'Error'
});
} else {
const { id } = customer;
}
});
</script>
</form>
I have been following the Stripe integration tutorial by Laracasts and it's become apparent to me that a lot has changed since Laravel 5.4 was released. I have been able to still find my way along but I have hit a bump trying to submit a payment form using Vue and Axios.
The product is being retrieved from a database and displayed in a select dropdown - this works. My issue is the data is not being properly sent to the store function in the PurchasesController. When I try to make a purchase the form modal appears fine, I fill it out with the appropriate test data and submit it, but in Chrome inspector I can see that /purchases returns a 404 error and when I check the network tab the error is: No query results for model [App\Product]
Here is the original Vue code:
<template>
<form action="/purchases" method="POST">
<input type="hidden" name="stripeToken" v-model="stripeToken">
<input type="hidden" name="stripeEmail" v-model="stripeEmail">
<select name="product" v-model="product">
<option v-for="product in products" :value="product.id">
{{ product.name }} — ${{ product.price /100 }}
</option>
</select>
<button type="submit" #click.prevent="buy">Buy Book</button>
</form>
</template>
<script>
export default {
props: ['products'],
data() {
return {
stripeEmail: '',
stripeToken: '',
product: 1
};
},
created(){
this.stripe = StripeCheckout.configure({
key: Laravel.stripeKey,
image: "https://stripe.com/img/documentation/checkout/marketplace.png",
locale: "auto",
token: function(token){
axios.post('/purchases', {
stripeToken: token.id,
stripeEmail: token.email
})
.then(function (response) {
alert('Complete! Thanks for your payment!');
})
.catch(function (error) {
console.log(error);
});
}
});
},
methods: {
buy(){
let product = this.findProductById(this.product);
this.stripe.open({
name: product.name,
description: product.description,
zipCode: true,
amount: product.price
});
},
findProductById(id){
return this.products.find(product => product.id == id);
}
}
}
</script>
And my PurchasesController.
<?php
namespace App\Http\Controllers;
use Log;
use App\Product;
use Illuminate\Http\Request;
use Stripe\{Charge, Customer};
class PurchasesController extends Controller
{
public function store()
{
Log::info("Product Info: " . request('product'));
Log::info("Stripe Email: " . request('stripeEmail'));
Log::info("Stripe Token: " . request('stripeToken'));
$product = Product::findOrFail(request('product'));
$customer = Customer::create([
'email' => request('stripeEmail'),
'source' => request('stripeToken')
]);
Charge::create([
'customer' => $customer->id,
'amount' => $product->price,
'currency' => 'aud'
]);
return 'All done';
}
}
I realise that product isn't being passed through to /purchases above so I have tried this:
axios.post('/purchases', {
stripeToken: token.id,
stripeEmail: token.email,
product: this.product
})
Unfortunately I still get the same No query results for model [App\Product] error even with that. Is there another/better way of passing data from Vue/Axios that I could use instead? If anyone is able to assist it would be much appreciated.
Thank you in advance.
Edit
The solution was to recast this to be a new variable and it started functioning again. Here is the relevant portion of the Vue Code that worked for me:
created(){
let module = this; // cast to separate variable
this.stripe = StripeCheckout.configure({
key: Laravel.stripeKey,
image: "https://stripe.com/img/documentation/checkout/marketplace.png",
locale: "auto",
token: function(token){
axios.post('/purchases', {
stripeToken: token.id,
stripeEmail: token.email,
product: module.product
})
.then(function (response) {
alert('Complete! Thanks for your payment!');
})
.catch(function (error) {
console.log(error);
});
}
});
},
Are you sure when assigning the product ID to data (using product: this.product) that this keyword is really your Vue instance? You might need to bind it manually calling .bind(this) on the .post(...) call.
See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind
Trying to charge a customer for a product registration. I've been following this tutorial upskillcourses.com. Which sets up a subscription. I' just trying to create a charge for a product.
I keep getting this error:
Stripe::CardError in Roadregistrations::RegistrationsController#create
Cannot charge a customer that has no active card
I've setup the stripe.js file:
app/assets/javascripts/
/* global $, Stripe */
//Document ready.
$(document).on('turbolinks:load', function(){
var theForm = $('#payment-form');
var submitBtn = $('#form-submit-btn');
//Set Stripe public key.
Stripe.setPublishableKey( $('meta[name="stripe-key"]').attr('content') );
//When user clicks form submit btn,
submitBtn.click(function(event){
//prevent default submission behavior.
event.preventDefault();
submitBtn.val("Processing").prop('disabled', true);
//Collect the credit card fields.
var ccNum = $('#card_number').val(),
cvcNum = $('#card_code').val(),
expMonth = $('#card_month').val(),
expYear = $('#card_year').val();
//Use Stripe JS library to check for card errors.
var error = false;
//Validate card number.
if(!Stripe.card.validateCardNumber(ccNum)) {
error = true;
alert('The credit card number appears to be invalid');
}
//Validate CVC number.
if(!Stripe.card.validateCVC(cvcNum)) {
error = true;
alert('The CVC number appears to be invalid');
}
//Validate expiration date.
if(!Stripe.card.validateExpiry(expMonth, expYear)) {
error = true;
alert('The expiration date appears to be invalid');
}
if (error) {
//If there are card errors, don't send to Stripe.
submitBtn.prop('disabled', false).val("Register and Pay");
} else {
//Send the card info to Stripe.
Stripe.createToken({
number: ccNum,
cvc: cvcNum,
exp_month: expMonth,
exp_year: expYear
}, stripeResponseHandler);
}
return false;
});
//Stripe will return a card token.
function stripeResponseHandler(status, response) {
//Get the token from the response.
var token = response.id;
//Inject the card token in a hidden field.
theForm.append($('<input type="hidden" name="user[stripe_card_token]">').val(token));
//Submit form to our Rails app.
theForm.get(0).submit();
}
});
Which it seems like the token is not being submitted with the form.
Not sure if I need both of these in my users_controller.rb:
# Only allow a trusted parameter "white list" through.
def roadregistration_params
params.require(:user).permit(:first_name, :last_name, :company, :street, :city, :state, :zip, :email, :phone, :roadshowcity, :stripe_card_token, :comments)
end
protected
def configure_permitted_parameters
devise_parameter_sanitizer.permit(:sign_up) { |u| u.permit(:stripe_card_token, :password, :password_confirmation, :email, :first_name, :last_name, :company, :street, :city, :state, :zip, :phone, :roadshowcity, :comments) }
end
Then I have this in my user model:
attr_accessor :stripe_card_token
# If user passes validations (email, pass, etc.),
# Call stripe and tell stripe to set up a subscription
def save_with_registration
if valid?
#product_price = Objective.find(objective_id)
customer = Stripe::Customer.create(email: email, card: stripe_card_token, description: stripe_card_token.to_s)
charge = Stripe::Charge.create(
:customer => customer.id,
:amount => 9500,
:currency => "usd",
:description => "Roadshow Registration"
)
self.stripe_customer_token = customer.id
save!
end
end
customer = Stripe::Customer.create(email: 'example#gma.com')
=> #<Stripe::Customer:0x3ffd3a10e024 id=cus_A5CWbyto5ugmju> JSON: {
"id": "cus_A5CWbyto5ugmju",
"object": "customer",
"account_balance": 0,
"created": 1486585998,
"currency": null,
"default_source": null,
"delinquent": false,
"description": null,
"discount": null,
"email": "example#gma.com",
"livemode": false,
"metadata": {},
"shipping": null,
"sources": {"object":"list","data":[],"has_more":false,"total_count":0,"url":"/v1/customers/cus_A5CWbyto5ugmju/sources"},
"subscriptions": {"object":"list","data":[],"has_more":false,"total_count":0,"url":"/v1/customers/cus_A5CWbyto5ugmju/subscriptions"}
}
You just have to link card to the customer after creating the customer on stripe and before charging him:
customer = Stripe::Customer.create(email: email)
customer.sources.create(card: stripe_card_token) # <-- this
charge = Stripe::Charge.create(..
And I would advice to not send stripe_card_token in the customer object.
If you are on a version older than 2015-02-18 API version replace sources with cards.
Turbolinks was not being loaded in the app/assets/javascripts/application.js file. Fixing that issue allowed the stripe javascript to run and passed the stripe_card_token along.
Having a warm time trying to charge a card in Meteor. The error I get is: Exception while invoking method 'chargeCard' Error: Match error: Expected string, got object. I do get the modal where I typed in the email and card number but after pressing the pay button, in terminal I get the error message.
How to call the charge function properly? I cant find any tutorial that matches closely the way I implement it.
The setup is very basic. I also have jquery installed.
Template:
<template name="hello">
<form id="myForm">
<input type="text" id="amount" name="amount"/>
<input type="hidden" id="stripeToken" name="stripeToken"/>
<input type="hidden" id="stripeEmail" name="stripeEmail"/>
</form>
<hr>
<button id="customButton">Pay</button>
</template>
js:
if (Meteor.isClient) {
Template.hello.helpers({
});
Template.hello.events({
'click button': function (e) {
e.preventDefault();
var handler = StripeCheckout.configure({
key: 'pk_test_rand',
token: function(token) {
$("#stripeToken").val(token.id);
$("#stripeEmail").val(token.email);
$("#myForm").submit();
Meteor.call('chargeCard', token); // this seem not right?
}
});
// Showing the pop up Stripe dialog
var amount = $("#amount").val() *100;
// Open Checkout with further options
handler.open({
name: 'Demo Site',
description: '2 widgets ($20.00)',
amount: amount
});
// Close Checkout on page navigation
$(window).on('popstate', function() {
handler.close();
});
}
});
Meteor.startup(function(){
$.getScript('https://checkout.stripe.com/checkout.js', function(){
// script has loaded
});
});
}
if (Meteor.isServer) {
Meteor.methods({
'chargeCard': function(stripeToken) {
check(stripeToken, String);
var Stripe = StripeAPI('sk_test_rand');
Stripe.charges.create({
source: stripeToken,
amount: 5000, // this is equivalent to $50
currency: 'usd'
}, function(err, charge) {
console.log(err, charge);
});
}
});
}
It seems you're passing the whole token object:
Meteor.call('chargeCard', token);
But your chargeCard() method expects a string:
check(stripeToken, String);
So you need to either pass only the token id:
Meteor.call('chargeCard', token.id);
or change your chargeCard() method to expect and use the whole token object:
Meteor.methods({
'chargeCard': function(stripeToken) {
check(stripeToken, Object);
var Stripe = StripeAPI('sk_test_rand');
Stripe.charges.create({
source: stripeToken.id,
...
I'm following this example integration from Stripe Docs (slightly modified in order to be able to add click handlers to more than one button:
<script src="https://checkout.stripe.com/checkout.js"></script>
<button id="customButton">Purchase</button>
<script>
var handler = StripeCheckout.configure({
key: 'pk_test_jPVRpCB1MLjWu2P71eTvXBZD',
image: '/square-image.png',
token: function(token) {
// Use the token to create the charge with a server-side script.
// You can access the token ID with `token.id`
}
});
$('.pay-deposit').click( function(e) {
// Open Checkout with further options
handler.open({
name: 'Demo Site',
description: '2 widgets ($20.00)',
amount: 2000
});
e.preventDefault();
});
In my particular case I have a few buttons like:
<button class='pay-deposit' booking-id='3455'>Pay Deposit</button>
<button class='pay-deposit' booking-id='335'>Pay Deposit</button>
<button class='pay-deposit' booking-id='34'>Pay Deposit</button>
... and obviously I'd like to pass a booking-id of clicked button somehow to token callback. Couldn't find any example or explanation covering this seemingly simple case... any help much appreciated. thanks!
This is a little bit late, but maybe it will help someone else. This is modified from a Rails example:
# HTML file
<script src="https://checkout.stripe.com/checkout.js"></script>
<button class='pay-deposit' data-booking-id='3455'>Pay Deposit</button>
<button class='pay-deposit' data-booking-id='335'>Pay Deposit</button>
<button class='pay-deposit' data-booking-id='34'>Pay Deposit</button>
# JS file
$('.pay-deposit').on('click', function(event) {
event.preventDefault();
// Get booking information from database
var booking_id = $(this).data('booking-id');
$.getJSON("/bookings/"+booking_id, function(data) {
// Open Checkout with further options
handler = stripe_checkout(booking_id);
handler.open({
name: "My Bookings",
description: data["description"],
amount: data["amount"],
email: data["email"],
});
// Close Checkout on page navigation
$(window).on('popstate', function() {
handler.close();
});
});
});
function stripe_checkout(booking_id) {
var handler = StripeCheckout.configure({
key: 'pk_test_jPVRpCB1MLjWu2P71eTvXBZD',
token: function(token) {
// Send the charge through
$.post("/charges/create",
{ token: token.id, booking_id: booking_id }, function(data) {
if (data["status"] == "ok") {
window.location = "/some-url";
} else {
// Deal with error
alert(data["message"]);
}
});
}
});
return handler;
}
# Bookings controller
class BookingsController < ApplicationController
def show
#booking = Booking.find(params[:id])
attrs = #booking.attributes
attrs.merge!("email" => current_user.email)
respond_to do |format|
format.json { render json: attrs.to_json }
end
end
end
# Charges controller
class ChargesController < ApplicationController
def create
booking = Booking.find(params[:booking_id])
customer = Stripe::Customer.create(card: params[:token])
charge = Stripe::Charge.create(
customer: customer.id,
amount: booking.amount,
description: booking.description,
currency: 'usd'
)
if charge.paid
# Do some things for successful charge
respond_to do |format|
format.json { render json: {status: "ok"}.to_json }
end
else
respond_to do |format|
format.json { render json: {status: "fail", message: "Error with processing payment. Please contact support."}.to_json }
end
end
end
end
Move your token initializer from configure to open.
var handler = StripeCheckout.configure({
key: 'pk_test_jPVRpCB1MLjWu2P71eTvXBZD',
image: '/square-image.png'
});
$('.pay-deposit').click( function(e) {
var data = $(this).data('booking-id');
// Open Checkout with further options
handler.open({
name: 'Demo Site',
description: '2 widgets ($20.00)',
amount: 2000,
token: function(token) {
// here you go!
alert(data);
}
});
e.preventDefault();
});
And switch to:
<button class='pay-deposit' data-booking-id='3455'>Pay Deposit</button>
<button class='pay-deposit' data-booking-id='335'>Pay Deposit</button>
<button class='pay-deposit' data-booking-id='34'>Pay Deposit</button>