Error adding customers to subscription in stripe - javascript

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>

Related

Stripe billing Subscription not charging the "subsequent charges" after initial successful Payment

I have followed this documentation to implement the Stripe Recurring Payments, which is working fine for both non-3d-Secure card and 3d-Secure enabled cards. For initial payments i get the 3d-Secure Payment authentication Modal from Stripe to authenticate payments using 3D-Secure Test cards.
The subscription is created along with invoices that Stripe generates for me. Everything's seems to be OK, BUT for 3D-Secure Enabled (Stripe Testing Cards) the First Payment goes through successfully but the subsequent payments which i have set interval of 1Day (for testing) are "Failed" by Stripe. From what i understand Stripe requires Customer's Authentication again to continue the Subscription.
In this they said
When 3D Secure is encountered, configure your billing settings to send
a hosted link to your customer to complete the flow.
That's completely OK, But why they even require the authentication step again if the first payment is succeeded and the subscription is started? I have gone through all the related Docs but found nothing to prevent that.
What if Customer does not check his/her email to come back to my Application to authenticate the payments?
OR
Is there anything wrong with the code? By that i mean that Stripe should have saved the Payment Method i.e. Card Number so it does not require the Customer Authentication on subsequent charges every time the new billing cycle start(monthly/biannually).
I have searched on internet and found and answer saying
You are using stripe.createToken which does not support 3ds
authentication. You need to migrate your client side code to
stripe.createPaymentMethod which does support it
Is that it? Any response is appreciated
My Payment.js
// set your stripe publishable key
var stripe = Stripe('pk_test_key');
var elements = stripe.elements();
var style = {
base: {
color: '#32325d',
lineHeight: '18px',
fontFamily: '"Helvetica Neue", Helvetica, sans-serif',
fontSmoothing: 'antialiased',
fontSize: '16px',
'::placeholder': {
color: '#aab7c4'
}
},
invalid: {
color: '#fa755a',
iconColor: '#fa755a'
}
};
var cardNumber = elements.create('cardNumber', {
style: style
});
cardNumber.mount('#cardNumber');
var cardExpiry = elements.create('cardExpiry', {
style: style
});
cardExpiry.mount('#cardExpiry');
var cardCvc = elements.create('cardCvc', {
style: style
});
cardCvc.mount('#cardCVC');
var cardholderName = $('#custName').val();
var amount = $('#amount').val();
var cardButton = document.getElementById('makePayment');
cardButton.addEventListener('click', function(ev) {
// alert();
ev.preventDefault();
stripe.createToken(cardNumber).then(function(result) {
if (result.error) {
} else {
//$body.addClass("loading");
fetch('process.php', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
token_id: result.token.id
})
}).then(function(result) {
// Handle server response (see Step 3)
result.json().then(function(json) {
handleServerResponse(json);
//alert();
})
});
}
});
});
function handleServerResponse(response) {
if (response.error) {
// Show error from server on payment form
} else if (response.requires_action) {
// Use Stripe.js to handle required card action
var action = response.next_action;
if (action && action.type == 'redirect_to_url') {
window.location = action.redirect_to_url.url;
}
handleAction(response);
} else {
console.log("3D" + response);
}
}
function handleAction(response) {
var paymentIntentSecret = response.payment_intent_client_secret;
stripe.handleCardPayment(paymentIntentSecret).then(function(result) {
if (result.error) {
// Display error.message in your UI.
} else {
// The payment has succeeded. Display a success message.
alert('Payment succeeded!');
}
});
}
Process.php
<?php
require_once('stripe-php/init.php');
\Stripe\Stripe::setApiKey('sk_test_key');
$json_str = file_get_contents('php://input');
$json_obj = json_decode($json_str);
//print_r($json_obj->token_id);die;
//$intent = null;
try {
if (isset($json_obj->token_id)) {
//Create Customer
$customer = \Stripe\Customer::create([
"email" => "test#gmail.com",
"name" => "Haroon",
"source" => $json_obj->token_id,
]);
//create product
$product = \Stripe\Product::create([
'name' => 'Water',
'type' => 'service',
]);
//create a plan
$plan = \Stripe\Plan::create([
'product' => $product->id,
'nickname' => 'Water',
'interval' => 'day',
'currency' => 'eur',
'amount' => 1200.00,
]);
//add subscription to stripe
$subscription = \Stripe\Subscription::create([
'customer' => $customer->id,
'items' => [[
"plan" => $plan->id],],
'expand' => ['latest_invoice.payment_intent'],
]);
}
$intent = \Stripe\PaymentIntent::retrieve($subscription->latest_invoice->payment_intent->id);
$subscription = \Stripe\Subscription::retrieve($subscription->id);
// $intent->confirm();
// }
generatePaymentResponse($intent,$subscription);
}catch (\Stripe\Error\Base $e) {
# Display error on client
echo json_encode([
'error' => $e->getMessage()
]);
}
function generatePaymentResponse($intent,$subscription) {
if ($intent->status == 'requires_action' && $subscription->status == 'incomplete' &&
$intent->next_action->type == 'use_stripe_sdk' ) {
# Tell the client to handle the action
echo json_encode([
'requires_action' => true,
'payment_intent_client_secret' => $intent->client_secret
]);
} else if ($intent->status == 'succeeded' && $subscription->status == 'active') {
# The payment didn’t need any additional actions and completed!
# Handle post-payment fulfillment
echo json_encode([
'success' => true
]);
} else if ($intent->status == 'requires_payment_method' && $subscription->status == 'incomplete') {
echo "Subscription failed";
} else {
# Invalid status
http_response_code(500);
echo json_encode(['error' => 'Invalid PaymentIntent status']);
}
}
If you are doing this for already exist card then i have done this just before some day . Refer this document https://stripe.com/docs/payments/3d-secure .
This suggest that you need to create paymentIntent and need to confirm this payment intent . and also in 3ds there are test card 4242 4242 4242 4242 This card does not require any authentication for 3ds . Giving below link for test cards
https://stripe.com/docs/payments/3d-secure/web#three-ds-cards

How to use ajax for login form

I am currently creating a login form in PHP PDO and I am using ajax to display the relevant messages on screen, e.g.
"Logging in..."
"Some input fields are empty"
"Your username is required"
"Your password is required"
Validation such as checking if input fields are empty is working fine along with when login credentials appear to be incorrect however when I login with correct credentials I just get message "Logging in..." and nothing happens, I don't even think it sets the session. I have also added a token to prevent CSRF and was just wondering if i'm using it correctly.
I'm unsure of what is causing my code not to proceed with logging in.
my ajax script:
<script type='text/javascript'>
$(document).ready(function () {
var submitButton = $("#btn-login");
submitButton.on('click', function (e) {
e.preventDefault();
// Get input field values of the contact form
var loginFormInputs = $('#login-form :input'),
userName = $('#txt_uname_email').val(),
userPassword = $('#txt_password').val(),
token = $('#token').val(),
alertMessage = $('#login-alert-message');
// Disable Inputs and display a loading message
alertMessage.html('<p style="opacity: 1"><i class="fa fa-spinner fa-spin text-success"></i> Logging in..</p>');
submitButton.html('<i class="fas fa-spinner fa-spin"></i>');
loginFormInputs.prop("disabled", true);
// Data to be sent to server
var post_data = {
'form': 'loginForm',
'userName': userName,
'userPassword': userPassword,
'token': token
};
// Ajax post data to server
$.post('./api', post_data, function (response) {
// Load jsn data from server and output message
if (response.type === 'error') {
alertMessage.html('<p><i class="fa-lg far fa-times-circle text-danger"></i> ' + response.text + '</p>');
submitButton.html('Login');
loginFormInputs.prop("disabled", false);
} else {
alertMessage.html('<p><i class="fa-lg far fa-check-circle text-success"></i> ' + response.text + '</p>');
submitButton.html('Login');
window.location = "dashboard";
}
}, 'json');
});
});
</script>
My login function (class.user.php) which is used in api.php:
public function doLogin($uname,$umail,$upass)
{
try
{
$stmt = $this->conn->prepare("SELECT * FROM `settings` LIMIT 1");
$stmt->execute();
$mainten=$stmt->fetch(PDO::FETCH_ASSOC);
$stmt = $this->conn->prepare("SELECT user_id, user_name, user_email, user_pass, status FROM users WHERE user_name=:uname OR user_email=:umail ");
$stmt->execute(array(':uname'=>$uname, ':umail'=>$umail));
$userRow=$stmt->fetch(PDO::FETCH_ASSOC);
if($stmt->rowCount() == 1)
{
if(password_verify($upass, $userRow['user_pass']))
{
session_regenerate_id(false);
return ["correctPass"=>true, "banned"=> ($userRow['status']== 1) ? true : false, "maintenance"=> ($mainten["maintenance"]== 1) ? true : false];
}
else
{
return ["correctPass"=>false];
}
}
}
catch(PDOException $e)
{
echo $e->getMessage();
}
}
api.php:
//include class.user.php here and set $login = new USER();
//set $uname, $umail, $upass, $token vars here
if( $_POST && $_POST["form"] === 'loginForm' ) {
// Use PHP To Detect An Ajax Request code is here
// Checking if the $_POST vars well provided, Exit if there is one missing code is here
// PHP validation for the fields required code is here
$validation = $login->doLogin($uname,$umail,$upass);
if($validation["correctPass"]){
if($validation["maintenance"]){
if (!in_array($uname, array('admin'))){
$output = json_encode(
array(
'type' => 'error',
'text' => 'Website under maintenance.'
));
die($output);
}
}
if($validation["banned"]){
$output = json_encode(
array(
'type' => 'error',
'text' => 'User has been banned.'
));
die($output);
}else{
if(Token::check($_POST['token'])) {
$stmtt = $login->runQuery("SELECT user_id FROM users WHERE user_name=:uname OR user_email=:umail ");
$stmtt->execute(array(':uname'=>$uname, ':umail'=>$umail));
$userRow=$stmtt->fetch(PDO::FETCH_ASSOC);
$_SESSION['user_session'] = $userRow['user_id'];
$output = json_encode(
array(
'type' => 'message',
'text' => 'Logged in successfully.'
));
die($output);
//$success = "Logged in successfully, redirecting..";
//header( "refresh:3;url=ab" );
//$login->redirect('dashboard');
} else {
$output = json_encode(
array(
'type' => 'error',
'text' => 'Unexpected error occured.'
));
die($output);
}
}
}
else{
$output = json_encode(
array(
'type' => 'error',
'text' => 'Incorrect username or password.'
));
die($output);
}
}

Sending data in Laravel using Axios & Vue

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

Using Laravel Mentions (at.JS and Laravel) to return A username and their ID

I'm currently building a comment system and would like to include the ability to mention users and tag them, subsequently I would like the tag to have a link to their profile (In this example, it's just /profile/{id})
I am currently using Laravel Mentions to generate the auto filled list from my Users Model. I'm doing it like:
function enableMentions(elem, type, column) {
$(elem).atwho({
at: "#",
limit: 5,
displayTpl: '<li><span>${name}</span></li>',
insertTpl: '${name}',
callbacks: {
remoteFilter: function(query, callback) {
if (query.length <= 1) return;
$.getJSON("/api/mentions/" + type, {
q: query,
c: column
}, function(data) {
callback(data);
});
}
}
});
}
// My api/mentions route: Route::get('/api/mentions/{type}', 'ApiController#index');
//My api controller:
public function index($type, Request $request)
{
try {
$resultColumns = [];
$query = $request->get('q');
$column = $request->get('c');
$model = app()->make(config('mentions.' . $type));
$records = $model->where($column, 'LIKE', "%$query%")
->get();
foreach ($records as $record) {
$resultColumns[] = $record->$column;
}
return response()->json($resultColumns);
} catch (\ReflectionException $e) {
return response()->json('Not Found', 404);
}
}
And finally, initializing the at.js
<script type="text/javascript">
$(function(){
enableMentions("#mention-commentContent", "users", "Username");
});
</script>
<div contentEditable="true" id="mention-commentContent" class="user-form" name="commentContent" type="text"></div>
I'm confused on how I can modify the above to return the username and the ID assigned to them, that way I can change the ${name} to ${id} and have it link through to their profile, right?
This package may help Laravel Mentions or this .
The basic idea is getting the usernames from the database via JSON , here is how they are using it in laracasts:
$("textarea#body").atwho({
at: "#", limit: 5, callbacks: {
remoteFilter: function (t, e) {
t.length <= 2 || $.getJSON("/api/users", {q: t}, function (t) {
e(t)
})
}
}
})

Stripe AJAX PHP and JavaScript

The below is my code I'm using for my Stripe Subscription Payments, because the site I'm implementing this into is AngularJS I want to keep the site from Refreshing so I'm opting for this AJAX option.
I have commented out a piece of the PHP which is
$charge = Stripe_Charge::create(array(
'customer' => $customer->id,
'amount' => $amount,
'currency' => 'gbp'
));
If I exclude this, the payment goes through once and I'm unsure how this application is able to process the charge if there is no call for it in the PHP file.
If I include the above snippet then the charge goes through twice.
My config.php only has require_once('Stripe.php'); along with my API keys.
So I'm hoping someone could explain why the charge goes through without the charge code piece in there if my code is actually okay for me to continue with.
HTML
<button id="proBtn">Subscribe</button>
JavaScript
Stripe.setPublishableKey('HIDDEN');
$('#proBtn').click(function(){
var token = function(res){
var $input = $('<input type="hidden" name="stripeToken" />').val(res.id);
var tokenId = $input.val();
var email = res.email;
setTimeout(function(){
$.ajax({
url:'/assets/lib/charge.php',
cache: false,
data:{ stripeEmail : email, stripeToken:tokenId, stripePlan: 'pro' },
type:'POST'
})
.done(function(data){
// If Payment Success
console.log(data);
$('#proBtn').html('Thank You').addClass('disabled');
})
.error(function(){
$('#proBtn').html('Error, Unable to Process Payment').addClass('disabled');
});
},500);
//$('form:first-child').append($input).submit();
};
StripeCheckout.open({
key: 'HIDDEN', // Your Key
address: false,
amount: 500,
currency: 'gbp',
name: 'Pro Account',
description: '',
panelLabel: 'Checkout',
allowRememberMe: false,
token: token
});
return false;
});
charge.php
<?php
require_once($_SERVER['DOCUMENT_ROOT'].'/assets/lib/config.php');
$token = $_POST['stripeToken'];
$email = $_POST['stripeEmail'];
$plan = $_POST['stripePlan'];
if ( $plan == 'pro' ) {
$amount = 500;
$amountFormat = number_format( $amount / 100, 2) ;
$plan = 'pro';
}
if ( $plan == 'team' ) {
$amount = 2000;
$amountFormat = number_format( $amount / 100, 2) ;
$plan = 'team';
}
Stripe_Plan::retrieve("pro");
Stripe_Plan::retrieve("team");
$customer = Stripe_Customer::create(array(
'email' => $email,
'card' => $token,
'plan' => $plan
));
try {
/*
$charge = Stripe_Charge::create(array(
'customer' => $customer->id,
'amount' => $amount,
'currency' => 'gbp'
));*/
echo 'success';
} catch(Stripe_CardError $e) {
echo "The card has been declined";
echo $token;
}
print_r($token);
echo '<br/>';
print_r($email);
echo '<br/>';
echo $customer->id;
echo '<h1>Successfully charged '.$amountFormat.'!</h1>';
?>
When you create a customer in Stripe it automatically charges them if you pass in a plan. You're basically saying create this customer and sign them up for this plan. That's why you are charging them twice.
Here's a link to the Customer API:
https://stripe.com/docs/api#create_customer

Categories

Resources