How to get charge id after creating a subscription using Stripe? - php

I am using Stripe as a payment gateway. Now there's a big problem bothers me.
I used code below to create a subscription:
<?php
require_once('lib/Stripe.php');
Stripe::setApiKey(API_KEY);
$token = $_POST['stripeToken'];
$customer = Stripe_Customer::create(array(
"card" => $token,
"plan" => $_POST['plan'],
"email" => "fakeuser#gmail.com",
));
This works fine, but I can not get Charge ID from $customer, and I found out there's no way in Stripe API to get it.
How to get it when creating a subscription? I really need Charge ID.

This is exactly what Stripe's webhooks are for. After creating a customer with an initial subscription, you'll get six webhook notifications:
customer.created, with the customer data (which you already have if you're saving what the API returns)
charge.succeeded (or charge.failed), which contains the initial charge data you're looking for
invoice.created, which is the associated invoice
invoice.payment_succeeded (or invoice.payment_failed), also telling you the status of the charge
customer.card.created, with the details of the new card
customer.subscription.created, with the details of the customer's subscription.
Stripe's API, like many APIs and many payment solutions, is built to be used with webhooks. If you're not taking advantage of webhooks, you're going to be missing functionality, and you're probably working too hard for what can be done without webhooks.
Stripe works to deliver the data to you. If you're writing code to poll Stripe, you're working way, way too hard.

I just ran into the same issue myself. I'm using the python library but the answer is more about Stripe's API than what language the client is in.
Ultimately, since I'm creating a new customer with each subscription, I was able to look up the invoice against the customer_id and grab its charge id. Here's what the python code for that looks like:
stripe_api.Invoice.all(customer=subscribe_result['customer'])['data'][0]['charge']
Again, note that this method would not work if you re-use customers, only if creating new customers with each subscription create.
It's certainly not ideal. It would be far better if the charge id were included in the return. Even knowing the Invoice ID would at least solve the issue of re-using customers though it would still require an unnecessary API call to fetch the invoice.

Another option you can use if you need the charge id right away and can't wait for a webhook is use the latest_invoice field on the returned subscription object.
A python example:
inv_id = subscription.latest_invoice
inv = stripe.Invoice.retrieve(inv_id)
charge_id = inv.charge

Well there is no straight-forward way to do this. There is however a hack to get charge_id for that subscription with out waiting for invoice.payment_succeeded callback.
This how I did in Ruby, you can treat this as a pseudo code. May be you can do this using PHP APIs
# Lets not retrieve all the invoices
# use filter
lower_limit_date = DateTime.strptime(that_subscription.start.to_s, '%s') - 1.hour
upper_limit_date = 2.hours.from_now
list_object_of_all_invoices_in_range = Stripe::Invoice.all(
{
customer: customer_id,
date: {
gt: lower_limit_date.to_i, # Start TimeStamp
lt: upper_limit_date.to_i # End TimeStamp
}
})
particular_invoice = list_object_of_all_invoices_in_range.data.
keep_if { |s| s[:subscription] == that_subscription.id }.first
stripe_charge_id = particular_invoice.charge # gives charge_id
See structure of ListObject for Invoices

Came across this question while searching for how to extract subscription ID in Java. Turns out the new version of the API (1.7.1 for Java) has ID field built right into the subscription object. The same is probably true for the PHP API.

I was stuck over the same problem after spending some time it turns out to be pretty simple.
Step 1: Call retrieve all invoice API with subscription id as a parameter.
Example in PHP:
$invoice = \Stripe\Invoice::all(array("subscription" => your_subscription_id));
In $invoice you will get Charge ID and hence, you can get everything after that.

Related

implementing stripe with strong customer authentication

I am trying to create a word-press plugin with stripe that support strong customer authentication. charges with stripe is working fine for me. I have referred this link https://www.codexworld.com/stripe-payment-gateway-integration-php/ for creating payments. Any such reference link for sca implementation in stripe? Thanks in advance
I just moved my company over from the old Charges API to SCA. It wasn't pretty. I'm a back-end engineer so I'm only going to tackle the back-end components needed to make this work. If you want a fully customised front-end too, you're going to need to read the Stripe docs and do a lot more bounces around form submit -> enhanced validation -> collect more data -> submit more data. This workflow was too complicated and thankfully my company went with the Stripe checkout solution for this part.
I work for a SaaS company and we take money for room bookings, so we have non-fungible time dependent 'stock' items. Since we sell booking slots on behalf of our customers we also have Stripe connected accounts in play. Basically, all the nightmare corner cases you could wish for. Since the checkout session is live for 24 hours we have to allocate then collect - if you start the process and then go for lunch there is no guarantee that when the payment is processed the room you want will still be free for your timeslot.
My process looks like:
Back-end calls Stripe and starts a checkout session with Session::create(). Set your payment_method_types and line_items in this as well as your return URLs. Also send up a payment_intent_data. Mine looks like this:
'payment_intent_data' => [
'transfer_data' => [
'destination' => 'acct_ number of linked account',
],
'capture_method' => 'manual',
'description' => 'description of item',
'statement_descriptor' => 'description of item, max 22 chars',
],
Obviously if you don't have connected accounts then omit the transfer_data but definitely include both descriptors. If you are sure of your stock levels (eg: digital goods) you can change your capture_method to automatic.
On a return from Stripe I send the session key (sk_) to the front-end who then hand this over to Stripe's payment form. This is the point you'll need to do a lot more work if you want a totally custom front-end. Good luck.
When the checkout session is successful I have a webhook event on checkout.session.completed to call my API, as well as handling the URL returns from the front-end. I keep extra data in my database about the payment state so I only handle each return once.
Either return leads me back to the session - the ID is in the front-end links and the session can be retrieved from $session = $event->data->object in the webhook callback. You can then get the intent out of the session with $intent = PaymentIntent::retrieve($session->payment_intent);
At this point, I handle the various status codes in the PaymentIntent;
requires_payment_method
requires_confirmation
requires_action
canceled
requires_capture
succeeded
The one I'm really interested in is requires_capture where I then check to see if the resource is still free. If it is, I $intent->capture() to finalise the payment and create the booking for the user. You can handle each of them as needed by your business process.
If you are dealing with connected accounts you will also need to load the transfer item associated with the PaymentIntent Charge object and then load the Charge referenced in the destination_payment field (you can load charges with the py_ key). Then set the description and statement_descriptor fields and save the charge back so your connected customers know what the payment is for.

Why Doesn't Stripe Checkout Add Customer Name to Customer Record?

I am trying to retrieve various pieces of data from a Stripe Checkout form submission, where I am just using the Stripe Checkout code provided in my Stripe Dashboard.
In my checkout_submission_completed event I have webhook where I am trying to retrieve email, name so that after successful purchase I can take other actions.
It is surprisingly difficult.
Here is how I can retrieve email (where payload is the response my webhook receives):
$cust = $payload['data']['object']['customer'];
$custdata = \Stripe\Customer::retrieve($cust);
$email=$custdata->email;
Ok, not a big deal.
How about Name? Well, here is where things get really fun. After hitting the form payment submit button Stripe creates a customer, completes a successful charge. But in the Customer object there is no name. Yes, no name. During a chat today with Stripe they had no explanation and said they would look into it more.
Turns out the only place where the name entered on the form shows up in a Stripe object is the Payment Details Object within the Payment Intent object.
I am serious. So here is how I am getting the name (using cust from previous code:
$piid = $cust = $payload['data']['object']['payment_intent'];
$pi = \Stripe\PaymentIntent::retrieve($piid);
$name = $pi['charges']['data'][0]['billing_details']['name'];
Is there a better way for me to do this?
thanks,
Brian
I think the idea is that the name collected is a cardholder name and is associated with the card [0] , not the Customer. A Customer might end up with multiple cards or other payment methods, and they might reasonably all have different cardholder names. So that information isn't transposed up to the Customer by default.
Your approach looks generally good — I would personally use the expand feature [1] of the API so you can skip a bunch of API calls by retrieving the full context of the Checkout Session and its payment and customer in one call from the webhook handler.
$session = \Stripe\Checkout\Session::retrieve(
$payload['data']['object']['id'],
["expand" => ["payment_intent", "customer"]]);
$cardholderName = $session['payment_intent']['charges']['data'][0]['billing_details']['name'];
\Stripe\Customer::update($session['customer'].id,
["name" => $cardholderName]);
[0] - https://stripe.com/docs/api/payment_methods/object?lang=php#payment_method_object-billing_details-name
[1] - https://stripe.com/docs/api/expanding_objects?lang=php

Stripe : Specify Specific Card for Subscription

I have set up subscription based website that allows people to have multiple subscriptions. I decided to go with Stripe for payment and card processing. It took very little time to get it integrated into my Symfony2 project. I was able to create subscriptions, customers, and add cards within a couple of hours. Then I ran into an issue. If a customer has multiple cards, I wanted to be able to allow them to choose which card they wanted to use when they create a new subscription. It sounded easy. After 2 days and about 30 hours of combing through their documentation I have to say that I cannot figure out how to get this to work.
The way I have this set up is that when the customer creates a card I store the "card id" in my database along with the brand. This just makes it easy to load details on the server side when the page is being requested. The customer creating the new subscription sees their cards and choose which one they want to use for the new subscription. This is passed to my php script via AJAX to create the new subscription. However, when I try to use a specific card, I am getting a 400 error indicating that the "card id" is not a token. I know that it is not a token since the token was used to add the card to the customer account but how in the world do I specify the exact card that the customer wants to use?
NOTE: Using an a new token creates another instance of the card.Not an option.
PHP:
require_once('../stripe-php/init.php');
//Set Secret API Key
\Stripe\Stripe::setApiKey("sk_test_XXXXXXXXXXXXXXXXXXXXX");
//Retrieve Customer
$cu = \Stripe\Customer::retrieve($_POST['customer_id']);
//Create Subscription using saved customer "card id"
$createSubscription = $cu->subscriptions->create(array("plan" => $_POST['sub_option'], "source" => $card));
POSTED TO STRIPE:
plan: "500-2016"
source: "card_xxxxxxxxxxxxxxxxxxxxx"
STRIPE ERROR: TYPE 400
error:
type: "invalid_request_error"
message: "No such token: card_xxxxxxxxxxxxxxxxxxxxxxx"
param: "source"
I got a reply from Stripe support on this: it is not possible for one Customer to have subscriptions with different payment sources. All invoices for subscriptions are always billed to the current default_source for the Customer. So if you change the default as Giles Bennett suggested, you'll be changing it for all subscriptions, regardless of what the default was at time of creation.
If you need one user to have subscriptions with more than one source, you need to create multiple stripe Customer objects for that user, with a different default_source for each.
Since I have not received any input from SO or Stripe, I have somewhat came to the conclusion that this cannot be done. I found a similar question on a different forum that ended with the results being - No Response From Stripe - and that this cannot be done. Though the Stripe documentation does not hit on this subject it does appear that a Subscription can only be charged to the default card. There is no "Card" object for subscriptions as there is for a "Charge".
I realise this thread is quite old, but having come across it whilst trying to answer the same question myself, this solution may be of use to those who come along afterwards.
The Stripe API says that the "source" parameter for the API call to create a new subscription is optional - if ommitted, then it will default to the customer's default card. If included, then it can only be a token (ie. a new card) or a dictionary entry (again, for a new card, just not tokenised).
The solution could be to update the customer's default source first. Using Cartalyst through Laravel, as we are :
$customer = Stripe::customers()->update( "customer_id", [
'default_source' => "card_id"
]);
You can then proceed to add your subscription as normal - the newly-defaulted card will be assigned to it. If needs be (depending on your application) then you may also wish to save the previous default card ID to a variable first, to then allow you to set it back to being the default card after your new subscription.
I hope that helps.

Updating Stripe plan customer number in PHP / CodeIgniter

This is my first time interacting with Stripe and trying to tie it into CodeIgniter.
Currently, when you create an account on my application it adds you to the correct Stripe plan and increases the total amount to be billed on that account.
I'm having a problem trying to work with this subscription plan #. I found in their documentation that this deals with the Subscription plans quantity.
What I need to happen is when you deactivate a user, it needs to update the quantity to 1 less. I can't figure this out.
Playing around with their API I was able to completely delete a subscribed customer by doing:
$this->load->library( 'stripe' );
$this->stripe->customer_delete('cus_1WS0pth6srNf7H');
Now I'm working with:
$this->load->library( 'stripe' );
$customer_id = $company->stripe_id;
$this->stripe->customer_update($customer_id, **WHAT GOES HERE?**);
Any help would be greatly appreciated. To sum this up, I need to update the quantity of my plan to -1 when a user is deactivated. The user is being deactivated fine in my application, but now I need to send a request to Stripe to update my plan to 1 less user.
Perhaps the customer_update function isn't the right one to use for this? There are a lot in the Stripe.php file.
Once I have this solved I can handle applying it to reactivating a user.
Here is the documentation where I think I need to be
I've been using Jason Horwitz's fork of bcessa's php-stripe library. It appears to be kept up to date with Stripe API changes.
https://github.com/sekati/php-stripe
Anyways, this is what I did for applying a plan to a customer
$request = $this->stripe->customer_subscribe( $stripe_id, $plan );
According to Stripe's API Docs, this will prorate the difference (if changing from one plan to another) by default so if that's what you want, you're good. If not, you can add it to the options array like so:
$request = $this->stripe->customer_subscribe( $stripe_id, $plan, array('prorate' => false) );
Not clear if you are using this library or not, but you should check:
https://github.com/bcessa/php-stripe
Written for CI & very simple. I'm going to assume you are.
According to Stripe support (I did this exact same thing recently), you need to actually make a unsubscribe & resubscribe to a new plan.
The steps I want through, roughly, were:
1) Get next invoice - so I could capture the next billing date for the new subscription (we used a trial period, so I needed this. you might be able to skip)
2) Unsubscribe the customer
3) Get my new total user count
4) Create a plan id (can be the same as you had before, I believe)
5) Subscribe with the new count to the new plan
6) Update anything on your own db

How do I set the Paypal Authorization Id when creating a new Sales Order in Netsuite

I am attempted to create a new Sales Order in Netsuite using PHPtoolkip_v2010.php and add a authorization id to its payment method.
I can create the Sales Order and set the payment method to PayPal, but I am unable to set the AuthId. If I try to set the Transaction Id I'm successful, but as soon as I switch the code to do an auth it fails.
Here's a snippet of the code I'm trying:
$sales_order_data = array(
'entity'=> new nsRecordRef(array('internalId' => $customer_id)),
'tranDate'=>date('c', strtotime($order_date)),
'itemList'=>array('item'=>array()),
);
//...
$sales_order_data['paymentMethod'] = array('internalId'=>7);
//$sales_order_data['payPalTranId'] = $paypal_transaction_id;
$sales_order_data['paypalAuthId'] = $paypal_transaction_id;
$sales_order = new nsComplexObject('SalesOrder', $sales_order_data);
$new_sales_order = $ns->add($sales_order);
If I get an existing Paypal order through the API and var_dump it I see the fields payPalTranId and paypalAuthId are set. Notice that the seconds P in Paypal is capitalized for the TranId but not for the AuthId. But I have tried both capped and uncapped for AuthId with no results.
It looks like what I wanted to do was impossible.
I've found this statement on from Celigo, a company that specializes in integrating Magento with Netsuite.
Both authorization and capture must be done on the Magento side.
NetSuite does not facilitate capture of payments that have been
authorized outside NetSuite. The NetSuite field that needs to be set
to indicate the authorization status, ‘PayPal Status’, is read-only
and thus cannot be set. The transaction cannot be processed unless
this field is set.
While they don't explicitly state that I can't set the AuthId, my goal in doing so was to authroize the payment outside of Netsuite and capture within. If that's not possible, then setting the AuthId is pointless for me.
Hope you got it straightened out and sucessfully.
Did you try to enable the "Override PayPal Settings " field?
I am trying to bring in PayPal information from external webstore and authid, looking at this field as reading your posting.

Categories