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.
I have 2 problem with WHMCS LocalAPI.
(1) I have a problem when I add order to client via LocalAPI.
I use a webservice where I must send my request to a web service and if returned true then I must add a order to client, but when I use addorder API the WHMCS apply credit automatically and it is not suitable for me. I want to add order and DO NOT paid invoice through credit.
(2)
I have a additional domain field in domainadditionalfields in whmcs database that fieldname as figure, but I do not know what syntax is true I use this code but value field is empty:
$command = 'AddOrder';
$postData = array(
'clientid' => 701,
'domain' => array($domain),
'domaintype' => array('register'),
'regperiod' => array($period),
'nameserver1' => $ns1,
'nameserver2' => $ns2,
'additionalfields["irnichandle"]' => "ABC",
'paymentmethod' => 'mailin',
'applycredit' => false,
);
$adminUsername = 'adminuser'; // Optional for WHMCS 7.2 and later
$results = localAPI($command, $postData, $adminUsername);
$orderid = $result['orderid'];
$invoiceid = $result['invoiceid'];
print_r($results);
(1) There is an option to disable auto applying credit, Setup > General Settings > Credit > Automatic Credit Use (untick it)
(2) For additional fields, you should use domainfields parameter in the AddOrder function as following:
'domainfields' => array(base64_encode(serialize(array($firstFieldValue))))
Docs:
Additional Domain Fields
Automatic Credit Application.
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.
I am using ignited/laravel-omnipay package for omnipay in laravel.
I am trying to implement token billing using stripe as shown here https://github.com/thephpleague/omnipay#token-billing.
Customer are getting created successfully on stripe but i am not able to make payment with the returned customer id.
Here's my code
$token = Input::get('stripetoken');
$gateway = Omnipay::create('Stripe');
$gateway->setApiKey('My Key');
$gateway->setTestMode(true);
$cardresponse = $gateway->createCard(array('token' =>$token))->send();
if ($cardresponse->isSuccessful()) {
$card_id = $cardresponse->getCardReference();
$data = $cardresponse->getData();
$customerid = $data['id'];
$cardid = $data['default_source'];
}
$paymentresponse = $gateway->purchase(array('amount' => '10.00','currency' => 'USD', 'cardReference' => $card_id))->send();
echo $paymentresponse->getMessage();
I am getting following response.
No such token: cus_8FwPaLNKdWcfRW
And when i check my stripe dashboard then customer with this id exists and has a card assigned.
Thanks for helping.
Since you're creating a customer object, you need to update your charge creation request to pass the customer ID in the customer parameter rather than in the source parameter (which causes the error you're seeing).
I'm not familiar with Omnipay but I think this should work:
$paymentresponse = $gateway->purchase(array('amount' => '10.00','currency' => 'USD', 'customerReference' => $card_id))->send();
I've recently implemented a PayPal IPN into CodeIgniter2, using the PayPal Lib. I'm using the system for subscriptions.
I have a table in my database that records all IPN requests in the database.
For some reason, after every sign up the IPN requests aren't coming through properly. I tend to get one subscr_payment along with several subscr_signups, all with the same subscr_id. It's causing untolds amount of hassle within the system, for obvious reasons. What adds to this, is the fact that the IPN requests don't come in the correct order, sometimes I get the subscr_payment before the subscr_signup - making it impossible to track as there's no subscr_id from the sign up to link it to a user.
I've had a Google and can't find much on this, I seem to be a little bit of an anomaly. I'm wondering if it's something to do with the PayPal Lib I'm using, but I don't really want to have to do it outside of CodeIgniter, as I am doing a lot of processing. Below is the full IPN script.
class Paypal extends CI_Controller {
function _construct()
{
parent::_construct();
$this->load->library('paypal_lib');
}
function ipn()
{
$this->output->enable_profiler(TRUE);
$this->load->model('payments_model');
$this->load->model('paypal_model');
$this->load->model('users_model');
ob_start();
if ($this->paypal_lib->validate_ipn())
{
$paypal_id = $this->paypal_model->add_paypal_ipn($this->paypal_lib->ipn_data);
// Split the 'custom' field up, containing ID of temp user, ID of package and coupon
$custom = explode(';', $this->paypal_lib->ipn_data['custom']);
###
# subscription sign up
###
if($this->paypal_lib->ipn_data['txn_type'] == 'subscr_signup') {
// Activate user/move from temp > live
$this->users_model->move_temp($custom[0], $this->paypal_lib->ipn_data['subscr_id']);
} # end subscr_signup
###
# subscription payment
###
if($this->paypal_lib->ipn_data['txn_type'] == 'subscr_payment') {
// Grab the coupon info, if we have one
$discount = 1;
if(!empty($custom[2])){
$this->load->model('coupons_model');
$couponinfo = $this->coupons_model->get_coupon($custom[2]);
$discount = $couponinfo->discount;
}
// Grab the package info
$package = $this->packages_model->get_package($custom[1]);
$price = $package->monthly * $discount; // Calculate discount, 0.8 = 20% off
// Does the price calculated match the gross price? If not something fishy is going on, block it
if($price != $this->paypal_lib->ipn_data['mc_gross']){
mail(CONTACT_EMAIL, SITE_NAME.' failed payment attempt, possible hack', 'Price paid doesnt match price computed... paid: '.$this->paypal_lib->ipn_data['mc_gross'].' - price worked out: '.$price."\n\n".print_r($this->paypal_lib->ipn_data, true));
exit;
}
// Grab the user's details based on the subscr_id
$user = $this->users_model->get_user_by_subscr_id($this->paypal_lib->ipn_data['subscr_id']);
// Add payment to the payments table
$data = array(
'user_id' => $user->user_id,
'subscr_id' => $user->subscr_id,
'txn_id' => $this->paypal_lib->ipn_data['txn_id'],
'amount' => $this->paypal_lib->ipn_data['mc_gross'],
'package_id' => $custom[1],
'coupon' => (empty($custom[2]) ? '' : $custom[2])
);
$this->payments_model->add_payment($data);
// Set (forced) user as active, and update their current active package
$data1 = array(
'package_id' => $custom[1],
'active' => 1
);
$this->users_model->update_user($data1, $user->user_id);
} # end subscr_payment
###
# subscription failed/cancelled
###
if($this->paypal_lib->ipn_data['txn_type'] == 'subscr_cancel' || $this->paypal_lib->ipn_data['txn_type'] == 'subscr_failed') {
// Grab user
$user = $this->users_model->get_user_by_subscr_id($this->paypal_lib->ipn_data['subscr_id']);
// Make user inactive
$data = array('active' => 0);
$this->users_model->update_user($data, $user->user_id);
} # end subscr_cancel|subscr_failed
###
# subscription modified/payment changed
###
if($this->paypal_lib->ipn_data['txn_type'] == 'subscr_modify') {
// Grab the coupon info, if we have one
$discount = 1;
if(!empty($custom[2])){
$this->load->model('coupons_model');
$couponinfo = $this->coupons_model->get_coupon($custom[2]);
$discount = $couponinfo->discount;
}
// Grab the package info
$package = $this->packages_model->get_package($custom[1]);
$price = $package->monthly * $discount; // Calculate discount, 0.8 = 20% off
// Does the price calculated match the gross price? If not something fishy is going on, block it
if($price != $this->paypal_lib->ipn_data['mc_gross']){
mail(CONTACT_EMAIL, SITE_NAME.' failed payment attempt, possible hack', 'Price paid doesnt match price computed... paid: '.$this->paypal_lib->ipn_data['mc_gross'].' - price worked out: '.$price."\n\n".print_r($this->paypal_lib->ipn_data, true));
exit;
}
// Grab the user's details based on the subscr_id
$user = $this->users_model->get_user_by_subscr_id($this->paypal_lib->ipn_data['subscr_id']);
// Add payment to the payments table
$data = array(
'user_id' => $user->user_id,
'subscr_id' => $user->subscr_id,
'txn_id' => $this->paypal_lib->ipn_data['txn_id'],
'amount' => $this->paypal_lib->ipn_data['mc_gross'],
'package_id' => $custom[1],
'coupon' => (empty($custom[2]) ? '' : $custom[2])
);
$this->payments_model->add_payment($data);
// Set (forced) user as active, and update their current active package
$data1 = array(
'package_id' => $custom[1],
'active' => 1
);
$this->users_model->update_user($data1, $user->user_id);
} # end subscr_modify
}
}
Below is an example of the calls made to my IPN for each transaction (CSV).
paypal_id,txn_id,subscr_id,txn_type,created
1,NULL,I-FMUK0B5KJWKA,subscr_signup,2011-02-03 16:19:43
2,9XM95194MM564230E,I-FMUK0B5KJWKA,subscr_payment,2011-02-03 16:19:45
3,NULL,I-FMUK0B5KJWKA,subscr_signup,2011-02-03 16:19:57
4,NULL,I-FMUK0B5KJWKA,subscr_signup,2011-02-03 16:20:19
6,NULL,I-FMUK0B5KJWKA,subscr_signup,2011-02-03 16:21:03
7,NULL,I-FMUK0B5KJWKA,subscr_signup,2011-02-03 16:22:25
8,NULL,I-FMUK0B5KJWKA,subscr_signup,2011-02-03 16:25:08
10,NULL,I-FMUK0B5KJWKA,subscr_signup,2011-02-03 16:30:33
12,NULL,I-FMUK0B5KJWKA,subscr_signup,2011-02-03 16:41:16
14,NULL,I-FMUK0B5KJWKA,subscr_signup,2011-02-03 17:02:42
16,NULL,I-FMUK0B5KJWKA,subscr_signup,2011-02-03 17:45:26
Consider this - PayPal is insert profanity. Now revisit the problem.
The chances are this isn't your fault, or CodeIgniter's or the Library's. PayPal is very bad at giving data in a uniform and timely manner, it is also slow and doesn't link data together very well.
My advice to you is save everything into an IPN table whenever a callback is made, even email yourself when ever an IPN call is made. Then work to try and figure out what PayPal is actually sending you, what you want and throw out the rest.
I think an IPN call is made even if the transaction has nothing to do with your web site. So if your Grandma sends you your Christmas money via PayPal it'll appear on the IPN callback.
Hope that helps a bit.
paypal isn't exactly easy to use but let me share 3 tips totackle the problems you are facing.
1) Create a table to store all IPN response from PayPal. Make sure you have a column called "raw" that stores EVERYTHING... do "json_encode($this->paypal_lib->ipn_data)". This will save you... since you can later write a script to pull out data from the raw column into it's own column down the road. This also helps with debugging.
2) For a start just pull out what is necessary out into columns of the ipn table so you can query easily... here's everything I've deem necessary for my use case which is probably similar to yours.
$this->payment_model->create_ipn(array(
'invoice' => $this->paypal_lib->ipn_data['invoice'],
'txn_type' => $this->paypal_lib->ipn_data['txn_id'],
'parent_txn_id' => $this->paypal_lib->ipn_data['parent_txn_id'],
'txn_type' => $this->paypal_lib->ipn_data['txn_type'],
'item_name' => $this->paypal_lib->ipn_data['item_name'],
'item_number' => $this->paypal_lib->ipn_data['item_number'],
'quantity' => $this->paypal_lib->ipn_data['quantity'],
'exchange_rate' => $this->paypal_lib->ipn_data['exchange_rate'],
'settle_amount' => $this->paypal_lib->ipn_data['settle_currency'],
'settle_amount' => $this->paypal_lib->ipn_data['settle_amount'],
'mc_currency' => $this->paypal_lib->ipn_data['mc_currency'],
'mc_fee' => $this->paypal_lib->ipn_data['mc_fee'],
'mc_gross' => $this->paypal_lib->ipn_data['mc_gross'],
'payment_date' => $this->paypal_lib->ipn_data['payment_date'],
'payment_status' => $this->paypal_lib->ipn_data['payment_status'],
'payment_type' => $this->paypal_lib->ipn_data['payment_type'],
'pending_reason' => $this->paypal_lib->ipn_data['pending_reason'],
'reason_code' => $this->paypal_lib->ipn_data['reason_code'],
'subscr_id' => $this->paypal_lib->ipn_data['subscr_id'],
'subscr_date' => $this->paypal_lib->ipn_data['subscr_date'] ? mdate('%Y-%m-%d %H:%i:%s', strtotime($this->paypal_lib->ipn_data['subscr_date'])) : NULL,
'subscr_effective' => $this->paypal_lib->ipn_data['subscr_effective'] ? mdate('%Y-%m-%d %H:%i:%s', strtotime($this->paypal_lib->ipn_data['subscr_effective'])) : NULL,
'period1' => $this->paypal_lib->ipn_data['period1'],
'period2' => $this->paypal_lib->ipn_data['period2'],
'period3' => $this->paypal_lib->ipn_data['period3'],
'amount1' => $this->paypal_lib->ipn_data['amount1'],
'amount2' => $this->paypal_lib->ipn_data['amount2'],
'amount3' => $this->paypal_lib->ipn_data['amount3'],
'mc_amount1' => $this->paypal_lib->ipn_data['mc_amount1'],
'mc_amount2' => $this->paypal_lib->ipn_data['mc_amount2'],
'mc_amount3' => $this->paypal_lib->ipn_data['mc_amount3'],
'recurring' => $this->paypal_lib->ipn_data['recurring'],
'reattempt' => $this->paypal_lib->ipn_data['reattempt'],
'retry_at' => $this->paypal_lib->ipn_data['retry_at'] ? mdate('%Y-%m-%d %H:%i:%s', strtotime($this->paypal_lib->ipn_data['retry_at'])) : NULL,
'recur_times' => $this->paypal_lib->ipn_data['recur_times'],
'payer_id' => $this->paypal_lib->ipn_data['payer_id'],
'payer_email' => $this->paypal_lib->ipn_data['payer_email'],
'payer_status' => $this->paypal_lib->ipn_data['payer_status'],
'payer_business_name' => $this->paypal_lib->ipn_data['payer_business_name'],
'ipn_track_id' => $this->paypal_lib->ipn_data['ipn_track_id'],
'raw' => json_encode($this->paypal_lib->ipn_data_arr),
'test_ipn' => $this->paypal_lib->ipn_data['test_ipn']
));
don't copy my code above since it's only meant to give you some rough ideas... if you do adapt my code do also ensure ipn_data function is like this (else you will get tons of errors)
function ipn_data($key)
{
return isset($this->fields[$key]) ? $this->fields[$key] : NULL;
}
to understand all the possible stuff they can send back this link is gold
https://cms.paypal.com/us/cgi-bin/?cmd=_render-content&content_ID=developer/e_howto_html_IPNandPDTVariables
but ^sigh^ don't trust it to be updated. i have found inconsistencies in what they doc says and what they sent back to me.
3) OK this i have to admit is another silly thing that paypal does - they don't give you an IPN date even tho' they don't guarantee the order in which it arrives at your server. For subscr_payment they give you payment_date... for subscr_signup they give you subscr_date... so what you need to do to get your IPN in the correct order is to have a column called ipn_date.
'ipn_date' => isset($this->paypal_lib->ipn_data['payment_date']) ?
mdate('%Y-%m-%d %H:%i:%s', strtotime($this->paypal_lib->ipn_data['payment_date'])) :
(isset($this->paypal_lib->ipn_data['subscr_date']) ?
mdate('%Y-%m-%d %H:%i:%s', strtotime($this->paypal_lib->ipn_data['subscr_date'])) : NULL),
now all is cool, you can "order by ipn_date" and I assure you everything will be in the correct order.
p.s. note the my first example code doesn't have this column, but it IS meant to be there. i'm just copying and pasting my development code to give you an idea.
What I do is ignore the signup ones and just process (create new user etc) on the actual payment transaction. And I wouldn't bother storing all those IPN trans. Have your IPN script send you an EMail on every one of them though, with an echo of all the fields posted. Then you'll have a record of them.