I have a simple form that registers users. The form also uses Stripe to process payments. Stripe's API is super clean but for some reason I can't seem to figure this out. Please tell me if i'm doing something wrong here.
Right now I'm in a test account with Stripe, but every time I register a user in stripe it keeps creating a new plan. I know in my code below I have "Stripe\Plan::create" that's because I don't know how to add a new customer to an existing plan. Is this normal the way I'm doing this currently?
In the screenshot below you will see that it will create a new plan for that customer and assign them to it. Is it possible to just keep adding new customers to an existing plan?
// Include configuration file
require_once 'stripe/config.php';
// Include Stripe PHP library
require_once 'vendor/autoload.php';
$payment_id = $statusMsg = $api_error = '';
$ordStatus = 'error';
// Check whether stripe token is not empty
if(!empty($_POST['stripeToken'])){
// Retrieve stripe token, card and user info from the submitted form data
$token = $_POST['stripeToken'];
$users_email = $_POST['email'];
$first_name = $_POST['first_name'];
$last_name = $_POST['last_name'];
$full_name = $first_name.' '.$last_name;
// Set API key
\Stripe\Stripe::setApiKey(STRIPE_API_KEY);
// Add customer to stripe
$customer = \Stripe\Customer::create(array(
'email' => $users_email,
'name' => $full_name,
'source' => $token
));
// Create a plan
try {
$plan = \Stripe\Plan::create([
'amount' => 3000,
'currency' => 'usd',
'interval' => 'month',
// 'product' => ['name' => 'Standard Plan'],
'product' => 'prod_ktItFVvqgr4edf',
]);
}catch(Exception $e) {
$api_error = $e->getMessage();
}
if(empty($api_error) && $plan){
// Creates a new subscription
try {
$subscription = \Stripe\Subscription::create(array(
"customer" => $customer->id,
"items" => array(
array(
"plan" => $plan->id,
),
),
));
}catch(Exception $e) {
$api_error = $e->getMessage();
}
if(empty($api_error) && $subscription){
// Retrieve subscription data
$subsData = $subscription->jsonSerialize();
// Check whether the subscription activation is successful
if($subsData['status'] == 'active'){
// Subscription info
$subscrID = $subsData['id'];
$custID = $subsData['customer'];
$planID = $subsData['plan']['id'];
$created = date("Y-m-d H:i:s", $subsData['created']);
$status = $subsData['status'];
// Insert into database
// Insert into database
}else{
$statusMsg = "Subscription activation failed!";
}
}else{
$statusMsg = "Subscription creation failed! ".$api_error;
}
}else{
$statusMsg = "Plan creation failed! ".$api_error;
}
}else{
$statusMsg = "Error on form submission, please try again.";
}
To create a subscription with an existing plan, simply pass in that older plan's ID when creating the subscription instead of creating a new plan for each customer. In your // Create a plan block, either retrieve an existing plan and use that to create the subscription or create a new plan if needed. Or better yet, store the plan ID's in your own database for easier retrieval.
Related
It seems I am missing something when trying to handle some errors with Stripe in test mode with PHP.
Payment are successful when I enter a test card number like 4242424242424242 (for VISA) but I have no error and nothing is recorded when I enter a bad card number.
It is like if the button "Pay" was not working although it displays an error message if I use a real correct card number in test mode.
Does someone know what am I doing wrong here ? Thanks.
Here is the PHP code :
// Check whether stripe token is not empty
if(!empty($_POST['stripeToken'])){
// Retrieve stripe token, card and user info from the submitted form data
$token = $_POST['stripeToken'];
$name = $_POST['name'];
$email = $_POST['email'];
$card_number = $_POST['card_number'];
$card_exp_month = $_POST['card_exp_month'];
$card_exp_year = $_POST['card_exp_year'];
$card_cvc = $_POST['card_cvc'];
$itemPrice = $_POST['itemPrice'];
$itemNumber = $_POST['itemNumber'];
$Ticket_number = $_POST['Ticket_number'];
// Include Stripe PHP library
require_once 'stripe-php/init.php';
// Set API key
\Stripe\Stripe::setApiKey(STRIPE_API_KEY);
// Add customer to stripe
$customer = \Stripe\Customer::create(array(
'email' => $email,
'source' => $token
));
// Unique order ID
$orderID = strtoupper(str_replace('.','',uniqid('', true)));
// Convert price to cents
$itemPrice = ($itemPrice*100);
// Charge a credit or a debit card
$charge = \Stripe\Charge::create(array(
'customer' => $customer->id,
'amount' => $itemPrice,
'currency' => $currency,
'description' => $itemName,
'metadata' => array(
'order_id' => $orderID
)
));
// Retrieve charge details
$chargeJson = $charge->jsonSerialize();
// Check whether the charge is successful
if($chargeJson['amount_refunded'] == 0 && empty($chargeJson['failure_code']) && $chargeJson['paid'] == 1 && $chargeJson['captured'] == 1){
// Order details
$transactionID = $chargeJson['balance_transaction'];
$paidAmount = $chargeJson['amount'];
$paidCurrency = $chargeJson['currency'];
$payment_status = $chargeJson['status'];
// Include database connection file
include_once 'dbConnect.php';
$itemPrice = ($itemPrice/100);
$paidAmount = ($paidAmount/100);
// Insert tansaction data into the database
$sql = "INSERT INTO orders(name,email,card_number,card_exp_month,card_exp_year,item_name,item_number,item_price,item_price_currency,paid_amount,paid_amount_currency,txn_id,payment_status,created,modified) VALUES('".$name."','".$email."','".$card_number."','".$card_exp_month."','".$card_exp_year."','".$itemName."','".$itemNumber."','".$itemPrice."','".$currency."','".$paidAmount."','".$paidCurrency."','".$transactionID."','".$payment_status."',NOW(),NOW())";
$insert = $db->query($sql);
$payment_id = $db->insert_id;
// If the order is successful
if($payment_status == 'succeeded'){
$ordStatus = 'success';
$statusMsg = '';
}else{
$statusMsg = "Your Payment has Failed!";
}
}else{
print '<pre>';print_r($chargeJson);
$statusMsg = "Transaction has been failed!";
}
}else{
$statusMsg = "Error on form submission.";
}
?>
Thank you for your answers but in fact it was a dummy mistake : I did not declare an id to display my error. I had to add the following code in my form :
<p id="payment-status" role="alert"></p>
I cant tell if its working since theirs no log or way to see a error since the server handles it all and i'm very new to these languages. As far as I can tell paypal sandbox sends the ipn but im not sure what works after that all I know is it does not show up in the DataBase. I know the password and everything is configed correctly and the database is set up. Im also following this tutorial here if it helps https://www.evoluted.net/thinktank/web-development/paypal-php-integration
im just to mush of a noob to php to follow it correctly.
heres the script that sends and receives ipn
<?php
// For test payments we want to enable the sandbox mode. If you want to put live
// payments through then this setting needs changing to `false`.
$enableSandbox = true;
// Database settings. Change these for your database configuration.
$dbConfig = [
'host' => 'sql106.epizy.com',
'username' => 'hidden',
'password' => 'hidden',
'name' => 'epiz_23648301_rod'
];
// PayPal settings. Change these to your account details and the relevant URLs
// for your site.
$paypalConfig = [
'email' => 'hidden#gmail.com',
'return_url' => 'http://rod.rf.gd/payment-successful.php',
'cancel_url' => 'http://rod.rf.gd/payment-cancelled.php',
'notify_url' => 'http://rod.rf.gd/payments.php'
];
$paypalUrl = $enableSandbox ? 'https://www.sandbox.paypal.com/cgi-bin/webscr' : 'https://www.paypal.com/cgi-bin/webscr';
// Product being purchased.
$itemName = 'Token';
$itemAmount = 1;
// Include Functions
require 'functions.php';
// Check if paypal request or response
if (!isset($_POST["txn_id"]) && !isset($_POST["txn_type"])) {
// Grab the post data so that we can set up the query string for PayPal.
// Ideally we'd use a whitelist here to check nothing is being injected into
// our post data.
$data = [];
foreach ($_POST as $key => $value) {
$data[$key] = stripslashes($value);
}
// Set the PayPal account.
$data['business'] = $paypalConfig['email'];
// Set the PayPal return addresses.
$data['return'] = stripslashes($paypalConfig['return_url']);
$data['cancel_return'] = stripslashes($paypalConfig['cancel_url']);
$data['notify_url'] = stripslashes($paypalConfig['notify_url']);
// Set the details about the product being purchased, including the amount
// and currency so that these aren't overridden by the form data.
$data['item_name'] = $itemName;
$data['amount'] = $itemAmount;
$data['currency_code'] = 'USD';
// Add any custom fields for the query string.
//$data['custom'] = USERID;
// Build the query string from the data.
$queryString = http_build_query($data);
// Redirect to paypal IPN
header('location:' . $paypalUrl . '?' . $queryString);
exit();
} else {
// Handle the PayPal response.
// Create a connection to the database.
$db = new mysqli($dbConfig['host'], $dbConfig['username'], $dbConfig['password'], $dbConfig['name']);
// Assign posted variables to local data array.
$data = [
'item_name' => $_POST['item_name'],
'item_number' => $_POST['item_number'],
'payment_status' => $_POST['payment_status'],
'payment_amount' => $_POST['mc_gross'],
'payment_currency' => $_POST['mc_currency'],
'txn_id' => $_POST['txn_id'],
'receiver_email' => $_POST['receiver_email'],
'payer_email' => $_POST['payer_email'],
'custom' => $_POST['custom'],
];
// We need to verify the transaction comes from PayPal and check we've not
// already processed the transaction before adding the payment to our
// database.
if (verifyTransaction($_POST) && checkTxnid($data['txn_id'])) {
if (addPayment($data) !== false) {
// Payment successfully added.
}
}
}
heres the addPayment func
function addPayment($data) {
global $db;
if (is_array($data)) {
$stmt = $db->prepare('INSERT INTO `payments` (txnid, payment_amount, payment_status, itemid, createdtime) VALUES(?, ?, ?, ?, ?)');
$stmt->bind_param(
'sdsss',
$data['txn_id'],
$data['payment_amount'],
$data['payment_status'],
$data['item_number'],
date('Y-m-d H:i:s')
);
$stmt->execute();
$stmt->close();
return $db->insert_id;
}
return false;
}
Have the below code - works fine.. BUT... As the DB is updated to PAID before the charge, and if the charge is declined, the DB is still updated to PAID.
Want to achieve -
If charge successful and not declined, charge customer THEN update DB to PAID..
NOT
Update DB to PAID, then charge customer...
// Create connection
$conn = mysqli_connect($hostname, $username,$password, $database);
// Check connection
if (!$conn) {
die("Connection failed: " . mysqli_connect_error());
}
$sql = "UPDATE details SET PaymentStatus='PAID' WHERE MembershipNo ='12345'";
if (mysqli_query($conn, $sql)) {
// starting the session
require_once('config.php');
$token = $_POST['stripeToken'];
$email = $_POST['stripeEmail'];
$SOSamount = ($_SESSION['price']*100);
$FirstName = ($_SESSION['FirstName']);
$LastName = ($_SESSION['LastName']);
$customer = \Stripe\Customer::create(array(
'email' => $email,
'source' => $token,
'description' => "$FirstName $LastName - Membership"
));
$charge = \Stripe\Charge::create(array(
'customer' => $customer->id,
'amount' => $amount,
'currency' => 'aud',
'description' => "Membership - $FirstName $LastName"
));
mysqli_close($conn);
You should wrap all calls to Stripe's API in a try/catch block to handle possible errors.
You can find more information in Stripe's API reference:
https://stripe.com/docs/api/php#errors
https://stripe.com/docs/api/php#error_handling
Basically, your code should look like this:
try {
$customer = \Stripe\Customer::create(...);
$charge = \Stripe\Charge::create(...);
} catch(\Stripe\Error\Card $e) {
// The card was declined, display an error message to your customer
// and end execution
} catch (Exception $e) {
// Another error happened, display an error message to your customer
// and end execution
}
// The charge was completed successfully, update your DB
Thx altoids
Found this snippet, moved Query..
if ($charge->paid == true) {
$conn = mysqli_connect($hostname, $username, $password, $database);
$sql = "UPDATE details SET PaymentStatus='PAID' WHERE MembershipNo ='12345'";
mysqli_query($conn, $sql);
}
I have this credit card adding when a customer first signs up
// CREDIT CARD CODE (STRIPE)
$q_get_user = "select * from users where `id` = '$user_id' ";
$r_get_user = mysqli_query($conn,$q_get_user);
$get_user = mysqli_fetch_assoc($r_get_user);
if(1) {
\Stripe\Stripe::setApiKey("sk_live_9N676756776");
try {
$t = \Stripe\Token::create(
array(
"card" => array(
"name" => $get_user['first_name']." ".$get_user['last_name'],
"number" => $credit_num,
"exp_month" => $credit_month,
"exp_year" => $credit_year,
"cvc" => $credit_ccv
)
)
);
if($t->id != '') {
try {
$c = \Stripe\Customer::create(array(
"description" => "Customer for ".$get_user['email'],
"source" => $t->id)
);
if($c->id != '') {
$stripe_token_response = mysqli_real_escape_string($conn, json_encode($t));
$stripe_token_id = mysqli_real_escape_string($conn, $t->id);
$stripe_customer_response = mysqli_real_escape_string($conn, json_encode($c));
$stripe_customer_id = mysqli_real_escape_string($conn, $c->id);
$stripe_card_id = mysqli_real_escape_string($conn, $c->default_source);
}
} catch (Exception $e) {
//print_r($e->getMessage());
header('Location: /credits?error=cc&message='.urlencode($e->getMessage()));die;
}
}
} catch (Exception $e) {
//print_r($e->getMessage());
header('Location: /credits?error=cc&message='.urlencode($e->getMessage()));die;
}
}
// END - CREDIT CARD CODE (STRIPE)
How can I make it inside of it being for a new customer for it to add to an existing customer? Therefore the customer is adding a new card (they will have more than one)
You are sending card details through the API directly, which is probably not something you want to do. This means that you get the card numbers on your server which has some serious PCI compliance implications. I would strongly advise you to modify your integration so that you always tokenize the card details first by using Stripe.js or Stripe Checkout client-side to send the card details to Stripe directly and get a unique card token (tok_XXX) that you'd then send safely to your server to create the Customer or add as a Card.
You can find a description of the 'card update' process here; the only difference you need is that, instead of doing this to replace the card:
$cu->source = $_POST['stripeToken']; // obtained with Checkout
You want to do this to add a new one:
$customer->sources->create(array("source" => $t->id));
I'm looking to do the following sequence (using the Stripe API) when a customer submits a credit card:
Check if the user has a stripe customer id in their meta
If they don't, create a new customer, save the entered card to that user
If the user already has a customer id, check if the entered card is already one of their saved cards.
If it is, charge that card
If it's not, add the new card to the customer object, and then charge that card.
In my current code, Stripe is returning an invalid_request error upon trying to create the charge. Here's the section of code that relates:
//See if our user already has a customer ID, if not create one
$stripeCustID = get_user_meta($current_user->ID, 'stripeCustID', true);
if (!empty($stripeCustID)) {
$customer = \Stripe\Customer::retrieve($stripeCustID);
} else {
// Create a Customer:
$customer = \Stripe\Customer::create(array(
'email' => $current_user->user_email,
'source' => $token,
));
$stripeCustID = $customer->id;
//Add to user's meta
update_user_meta($current_user->ID, 'stripeCustID', $stripeCustID);
}
//Figure out if the user is using a stored card or a new card by comparing card fingerprints
$tokenData = \Stripe\Token::retrieve($token);
$thisCard = $tokenData['card'];
$custCards = $customer['sources']['data'];
foreach ($custCards as $card) {
if ($card['fingerprint'] == $thisCard['fingerprint']) {
$source = $thisCard['id'];
}
}
//If this card is not an existing one, we'll add it
if ($source == false) {
$newSource = $customer->sources->create(array('source' => $token));
$source=$newSource['id'];
}
// Try to authorize the card
$chargeArgs = array(
'amount' => $cartTotal,
'currency' => 'usd',
'description' => 'TPS Space Rental',
'customer' => $stripeCustID,
'source' => $source,
'capture' => false, //this pre-authorizes the card for 7 days instead of charging it immedietely
);
try {
$charge = \Stripe\Charge::create($chargeArgs);
Any help is appreciated.
The issue turned out to be this section:
if ($card['fingerprint'] == $thisCard['fingerprint']) {
$source = $thisCard['id'];
}
If a fingerprint match is successful, I need to grab the id of the card already in the users's meta, not the matching card that was inputted. So, this works:
if ($card['fingerprint'] == $thisCard['fingerprint']) {
$source = $card['id'];
}