Getting Stripe charge parameters from Error object - php

Im building a php site, using Stripe to do the billing. If a charge is successful, I log the results in a table, so far so good. If the charge fails (is declined) I want to do the same thing, log the message etc, but also log the amount and the currency. Is it possible to get these values from the Stripe\Error\Card object?
I seem to be able to get at the values using
catch(\Stripe\Error\Card $e) {
$test = $e->getTrace();
print_r($test[3]['args']);
}
but this looks pretty dodgy! I suppose I could just grab them from the original charge request, just wondered if there was another way?

I use try and catch for this and store the amount and currency in variables before the error, the same variables I was intending to send to stripe in a charge. For example:
$customer = \Stripe\Customer::create(array(
'email' => $customer_email,
'source' => $token
));
try {
$charge = \Stripe\Charge::create(array(
'customer' => $customer->id,
'amount' => $amount_in_cents,
'currency' => 'usd'
));
} catch(\Stripe\Error\Card $e) { // Your error handling code }
I hope this is helpful, I know it's not exactly what you wanted but if you're doing the charge and catching the error like this then you'll already have the amount and currency available to you and not need to look for it in the error card.

Related

Stripe PHP API : You cannot confirm this PaymentIntent because it's missing a payment method

I am trying to implement the Stripe PHP API, but I am stuck since last week.
At first I thought I did not mention the payment method correctly in the way of a correct notation.
However this seems not to be the case.
I have created a wrapper class that loads all stripe API files and following handles all calls required for me.
Hence that's why the code layout.
When I execute my script I receive the following error :
Fatal error: Uncaught (Status 400) (Request req_AQbkjKFB4mo31Z) You
cannot confirm this PaymentIntent because it's missing a payment
method. You can either update the PaymentIntent with a payment method
and then confirm it again, or confirm it again directly with a payment
method. thrown in
httpdocs/stripe/stripe/lib/Exception/ApiErrorException.php
on line 38
I have contacted the Stripe helpdesk already however they keep referring me to their manual but that doesn't get me any further either.
The iDeal payment method has been enabled in their dashboard. So that's not the reason for failure.
Where it COULD be is the manner I setup my payment. But for as far as I am concerned I do not have set the payment method correctly. But for as far as I can see that has been done accordingly their manual.
Also, I cannot find any confirmatory documentation about using the retrieve call. Should I do this? Or is this simply double and unneeded.
public function create_payment($amount, $order_id, $method = 'ideal', $return_url = NULL, $currency = 'eur'){
///######## CHECK IF CURRENCY IS ALLOWED
if(!$this->currency_supported($currency)) exit('<strong>error</strong>, stripe currency not supported : '.$currency);
///######## SETUP PAYMENT
$result = $this->obj->paymentIntents->create(
array(
'amount' => $amount,
'currency' => $currency,
'payment_method_types' => array($method)
)
);
///######## IF ERROR
if(!is_object($result) || !isset($result->id)) exit('<strong>error</strong>, something went wrong during stripe payment intend creation');
///######## SETUP PAYMENT ID
$payment_id = $result->id;
///######## RETRIEVE PAYMENT INTEND DETAILS
$result = $this->obj->paymentIntents->retrieve($payment_id, []);
///######## SET AN ORDER ID
$result = $this->obj->paymentIntents->update($payment_id, array(
'metadata' => array(
'order_id' => $order_id,
),
'payment_method_types' => array($method),
));
///######## SETUP PARAMETRES
$params = array('payment_method_types' => array($method));
///######## IF THE RETURN URL HAS BEEN SET
if($return_url) $params['return_url'] = $return_url;
///######## CONFIRM A PAYMENT INTEND
$result = $this->obj->paymentIntents->confirm($payment_id, $params);
exit(print_r($result));
}
I hope that one could point me at my error. Since I am absolutely stuck here.

How to add multiple cards in same customer in stripe payment gateway using php

i was implementing stripe payment in testing mode.Here i got an error like Same token is used again. is there any different way to add multiple cards.
or i need to call retrive function in a separate page so that conflict of same token never come again.And how should i set a card default.
public function createToken($data)
{
$TokenResult=Token::create(array(
"card" => array(
"name" => $data['name'],
"number" => $data['card_number'],
"exp_month" => $data['month'],
"exp_year" => $data['year'],
"cvc" => $data['cvc']
)));
//echo "<pre>";;
//print_r($TokenResult);
$this->token=$TokenResult['id'];//store token id into token variable
$this->chargeCard($this->token); //call chargecard function via passing token id
}
/*
* function to create customer
*/
public function createCustomer($data,$token=null)//pass form data and token id
{
$customer=Customer::create(array(
"email"=>$data['email'],
"description" => $data['name'],
"source" => $token // obtained with Stripe.js
));
$customerId=$customer['id'];
$this->retriveCustomer($customerId,$token);
}
/*
* function to retrive current customers for adding multiple cards to same customers*/
public function retriveCustomer($customerid,$token)
{
echo $this->token;
//die('here');
$retriveResult=Customer::retrieve($customerid);
$retriveResult->sources->create(array("source" =>$this->token));
return $retriveResult;
}
First, please note that unless you are PCI certified and allowed to directly manipulate card data, you should never have access to card numbers in your server-side code.
Card tokens should be created client-side, via Checkout or Elements. Your server should only deal with client-side created card tokens and never with PCI-sensitive information (card numbers and CVCs). This will greatly decrease the burden of PCI compliance and make you eligible for PCI SAQ A.
In PHP, this is how you'd add a card to an existing customer object:
$customer = \Stripe\Customer::retrieve("cus_...");
$card = $customer->sources->create(array(
"source" => $token // token created by Checkout or Elements
));
I think you dont need to create a token in case of adding new card. It helps while you update certain card. So the flow of addition will be same as you created for first card.
I dont know which stripe version you are using, I am using bit old:
$card_array = array(
'card' => array(
'number' => $number,
'exp_month' => $exp_month,
'exp_year' => $exp_year,
'cvc' => $cvc,
'name' => $name
)
);
$card_obj = $this->setData("\Stripe\Customer", "retrieve", $customer_id, TRUE);
$card = $card_obj->sources->create($card_array);
Stripe's docs don't explain a lot of the more nuanced procedures so you have to do a lot of testing.
Assuming you have a customer object $cu, with the token you get from checkout or whatever you use:
$card = $cu->sources->create(['source'=>$token]);
will add a card to the customer. It just adds the card to the list; subsequent calls will add cards to the list. Note that it does not check for duplicates, so the same card can be on the list multiple times. It will also not set the new card to the active card. To make a card the default (or active), use
$cu->default_source = $card
$cu->save();
Using the older card interface:
$cu->card = $token;
$cu->save();
The new card will replace the default card. It will NOT make the previously default card inactive; it will delete the current default and make the new card the active default. The card interface is the easiest if you're just allowing 1 card to be attached to a customer at a time.

Stripe doubles anything

I am using a function I created that I have tried creating customers from, and creating charges from. For whatever reason it seems to be double charging in test mode (Not bringing into live mode under these conditions) and I'm trying to understand why. I had it going through a few functions so I made it all happen in one function to make sure that it had nothing to do with what I had made. I'm lost on why this is happening. I try to make charges from token, doubles in less than a second. I try to create a customer from token, doubles in less than a second. I am using Stripes latest stripe-php library.
public function invoice($invoice = null) {
//Provides billing info for invoice.ctp
$this->loadModel('Invoice');
$billingi = $this->Invoice->get($invoice, [
'contain' => ['Items'],
]);
$dollars = 0;
foreach ($billingi->items as $item) {
$dollars += $item->price;
}
$cents = bcmul($dollars, 100);
$price = floatval($cents);
if ($this->request->is('post')) {
$stripeToken = $this->request->data('stripeToken');
//Sets stripe API
\Stripe\Stripe::setApiKey("sk_test_QVYouMViTf1k3zfVu2VAyZge");
//Retrieves stripe token from stripe API
//$response = \Stripe\Token::retrieve($stripeToken);
\Stripe\Customer::create(array(
"description" => "Test customer",
"source" => $stripeToken // obtained with Stripe.js
));
$this->Flash->success(__('Thank you for your payment!'));
return $this->redirect(['action' => 'approved', $invoice]);
}
/*
if ($response && $this->checkExists($response->card->cvc_check, $response->card->address_zip_check) == true) {
$this->insertCharge($invoice, $response, $price);
} else {
//Throw error because cvc_check or zip came back null (doesn't exist)
}
}
*/
$this->set('billingi', $billingi);
$this->set('_serialize', ['billing']);
}
The reason why there are things commented out is because I wanted to test the function without it, but adding it back later when I understand what the issue is.
In your code, the only API request sent to Stripe is a customer creation request (\Stripe\Customer::create(...)).
This doesn't charge the user -- it merely validates the card from the token in the source parameter, and creates a persistent customer object that you can in turn use to create actual charges. This tutorial explains this flow.
There's nothing in your code that would cause the API request to be sent twice. It's very unlikely the issue is on Stripe's end. More likely, your code is being called twice for some reason that's not related to Stripe. You'd need to add traces to your code to figure out what exactly is being called in what order.

PayPal Express Checkout with Omnipay not showing order in Sandbox account

I have used the Omnipay PayPal_Express checkout script on my site and everything works fine when I pay for an order except the order doesn't show in the PayPal Sandbox account.
It does show when I use the same script for PayPal_Pro.
My code is as follows:
use Omnipay\Omnipay;
// PayPal Express:
if(isset($_POST['paypalexpress'])) {
$gateway = GatewayFactory::create('PayPal_Express');
$gateway->setUsername('{myusername}');
$gateway->setPassword('{mypassword}');
$gateway->setSignature('{mysignauture}');
$gateway->setTestMode(true);
$response = $gateway->purchase(
array(
'cancelUrl'=>'http://www.mysite.com/?cancelled',
'returnUrl'=>'http://www.mysite.com/?success',
'amount' => "12.99",
'currency' => 'GBP',
'Description' => 'Test Purchase for 12.99'
)
)->send();
$response->redirect();
}
I have created two test accounts in my Sandbox, one is for the above API and one I use to pay with. I have tried paying with the test card details and the login but the order detail doesn't show in the account.
Can anyone help?
It looks like you're missing the completePurchase() part when Paypal returns to your returnUrl. My code assumes that you have the order details in a variable $order, but it may look something like this:
if(isset($_GET['success'])) {
$response = $gateway->completePurchase(array(
'transactionId' => $order->transaction,
'transactionReference' => $order->reference,
'amount' => $order->total,
'currency' => $order->currency,
))->send();
if ( ! $response->isSuccessful())
{
throw new Exception($response->getMessage());
}
}
Let me know if you need any help retrieving the order details on return. It can be stored in a session before you redirect, or in a database. If you haven't done already, take a look at the example code: https://github.com/omnipay/example/blob/master/index.php

Using Coupon Codes with Stripe

I have a site that is using Stripe to process a subscription payments. There is only one type of subscription.
I followed this tutorial on NetTuts to do the initial setup.
Had a form working fine processing subscriptions and everything worked. Client requested a coupon code. Stripe supports this so I set out trying to add a coupon code to the existing form.
I set up coupon codes in Stripe, set my testing keys and switched to test mode in stripe.
I'm performing a couple of checks in my code:
Check to see whether a coupon was entered, if not create a new customer object without a coupon option
Check to see whether the Coupon is valid, if not return an error
If there has been a coupon entered and it is valid, then pass the matching Stripe coupon object as an option when creating a new customer.
if(isset($couponCode) && strlen($couponCode) > 0) {
$using_discount = true;
try {
$coupon = Stripe_Coupon::retrieve($couponCode);
if($coupon !== NULL) {
$cCode = $coupon;
}
// if we got here, the coupon is valid
} catch (Exception $e) {
// an exception was caught, so the code is invalid
$message = $e->getMessage();
returnErrorWithMessage($message);
}
}
try
{
if($using_discount == true) {
$customer = Stripe_Customer::create(array(
"card" => $token,
"plan" => "basic_plan",
"email" => $email,
"coupon" => $cCode
));
}
else {
$customer = Stripe_Customer::create(array(
"card" => $token,
"plan" => "basic_plan",
"email" => $email
));
}
$couponCode is populated with the form field correctly the same way all other fields are populated, I've triple checked that it is being pulled correctly.
When I try to submit the form without a coupon code, it charges the full amount and passes through Stripe correctly.
However if I enter either a valid OR invalid coupon code, it does not pass a coupon object with the customer object when creating a new customer object and charges the full amount when passing through Stripe.
I've looked at the code for hours and can't seem to figure out why it is always failing to recognize the discount code and pass the matching coupon object to Stripe.
This is probably a little out dated and you found a solution for your code. But it would seem all you need to do is pass through your original $couponCode as the array value for Coupon. As stated by codasaurus you are just getting an array back from stripe of the coupon, which you don't need unless you did $cCode->id and pass the ID back to your array to create a customer.
I changed when you set the $using_discount to true, as this would trigger to send a coupon code if the coupon was valid or not.
Once the coupon is actually valid we then send the coupon. You only need the value of your submitted state so $coupon is the reference to the discount in there system. Or you could use the $coupon->id if you wanted to create it that way.
Here is my take on a solution based on your code, it could be better, but I hope it helps others looking for a solution like me.
if($coupon!='' && strlen($coupon) > 0) {
try {
$coupon = Stripe_Coupon::retrieve($coupon); //check coupon exists
if($coupon !== NULL) {
$using_discount = true; //set to true our coupon exists or take the coupon id if you wanted to.
}
// if we got here, the coupon is valid
} catch (Exception $e) {
// an exception was caught, so the code is invalid
$message = $e->getMessage();
returnErrorWithMessage($message);
}
}
if($using_discount==true)
{
$customer = Stripe_Customer::create(array(
"card" => $token,
"plan" => $plan,
"email" => $id,
"coupon"=>$coupon) //original value input example: "75OFFMEMBER" stripe should be doing the rest.
);
}
else
{
$customer = Stripe_Customer::create(array(
"card" => $token,
"plan" => $plan,
"email" => $id)
);
}
Looking at the documentation https://stripe.com/docs/api#create_customer, the Stripe_Customer::create() call is looking for just the coupon code. It looks like you're passing in the entire coupon object.
Unless your first try catch fails, $couponCode already has your coupon code. Also, there are several other checks you need to perform to determine if the coupon is valid. For example, if the coupon->times_redeemed < coupon->max_redemptions, or if the coupon->redeem_by has passed, etc. You might also want to check if the customer is already using the coupon by checking the customer discount object.
If any of these checks fail, just set your $using_discount = false;

Categories