Stripe making multiple customers with same email address - php

I have stripe check out with php. It creates customers and charges them. I want to create a donation form where if same customer comes back and gives with same email address that Stripe doesn't create another customer but charges the existing customer with additional payments. Is this possible? Or does the checkout always create new customer with new customer id?
Here is my charge.php
<?php
require_once('config.php');
$token = $_POST['stripeToken'];
if($_POST) {
$error = NULL;
try{
if(!isset($_POST['stripeToken']))
throw new Exception("The Stripe Token was not generated correctly");
$customer = Stripe_Customer::create(array(
'card' => $token,
'email' => $_POST['stripeEmail'],
'description' => 'Thrive General Donor'
));
$charge = Stripe_Charge::create(array(
'customer' => $customer->id,
'amount' => $_POST['donationAmount'] * 100,
'currency' => 'usd'
));
}
catch(Exception $e) {
$eror = $e->getMessage();
}
}
?>

You will need to store the relationship between email address and Stripe customer ID in a database. I've determined this by looking at Stripe's API on Customers.
First, when creating a new customer every field is optional. This leads me to believe that every single time you POST to /v1/customers, it will "[create] a new customer object."
Also, when retrieving a customer the only field available is the id. This leads me to believe that you cannot retrieve a customer based on an email address or other field.
If you can't store this information in a database, you can always list all the customers with GET /v1/customers. This will require you to paginate through and check all customer objects until you find one with a matching email address. You can see how this would be quite inefficient if done every time you tried to create a customer.

You can list all users for a given email address.
https://stripe.com/docs/api#list_customers
In JavaScript you could do this:
const customerAlreadyExists = (email)=>{
return doGet(email)
.then(response => response.data.length > 0);
}
const doGet = (url: string)=>{
return fetch('https://api.stripe.com/v1/customers' + '?email=' + email, {
method: 'GET',
headers: {
Accept: 'application/json',
Authorization: 'Bearer ' + STRIPE_API_KEY
}
}).then(function (response) {
return response.json();
}).catch(function (error) {
console.error('Error:', error);
});
}

Related

Notify frontend Vue SPA if Stripe (iDEAL) charge is succeeded with Webhooks and Jobs Laravel

So I'm building an webapp that has a shop with Laravel API and Vue as the frontend SPA.
I've been trying to use Strip to enable payments. So far, with the help of Stripe's documentation, I have been able to create a Source in the frontend. for iDEAL, Stripe highly suggests us to make use of webhooks to confirm whether a payment has succeeded. (I'm using Spatie/Laravel-Stripe-Webhook package) This is the current flow of my webapp:
Checkout.vue:
checkout() {
const sourceData = {
type: 'ideal',
amount: this.cart.total,
currency: 'eur',
owner: {
name: this.name + ' ' + this.last_name,
email: this.email,
},
metadata: {
order: JSON.stringify(order),
total_quantity: this.cart.total_quantity,
},
redirect: {
return_url: 'http://example.test/order-confirmation',
},
}
this.stripe.createSource(this.ideal, sourceData).then(function(result) {
if (result.error) {
console.log(error.message)
this.error = error.message
} else {
stripeSourceHandler(result.source)
}
})
const stripeSourceHandler = source => {
document.location.href = source.redirect.url
}
},
After filling in billing address, emails etc. the user starts the payment.
User gets redirected to iDEAL payment page where they can authorize payment.
The Source is now created. Stripe sends source.chargeable webhook:
config/stripe-webhooks.php:
'jobs' => [
'source_chargeable' => \App\Jobs\StripeWebhooks\ProcessPaymentsJob::class,
'charge_succeeded' => \App\Jobs\StripeWebhooks\ChargeSucceededJob::class,
],
ProcessPaymentsJob.php:
public function __construct(WebhookCall $webhookCall)
{
$this->webhookCall = $webhookCall;
}
public function handle()
{
$charge = $this->webhookCall->payload['data']['object'];
\Stripe\Stripe::setApiKey(config('services.stripe.secret'));
$user = '';
if(User::find(Auth::id())) {
$user = $user->name;
} else {
$user = 'a guest';
}
$payment = \Stripe\Charge::create([
'amount' => $charge['amount'],
'currency' => 'eur',
'source' => $charge['id'],
'description' => 'New payment from '. $user,
'metadata' => [
'order' => $charge['metadata']['order'],
'total_quantity' => $charge['metadata']['total_quantity'],
]
]);
}
User returns to redirect[return_url]
If all went well, Stripe should send charge.succeeded webhook:
ChargeSucceededJob.php:
public function __construct(WebhookCall $webhookCall)
{
$this->webhookCall = $webhookCall;
}
public function handle()
{
$charge = $this->webhookCall->payload['data']['object'];
$order = Order::create([
'user_id' => Auth::id() ?? null,
'payment_id' => $charge['id'],
'payment_method' => $charge['payment_method_details']['type'],
'billing_email' => $charge['billing_details']['email'],
'billing_name' => $charge['metadata']['name'],
'billing_last_name' => $charge['metadata']['last_name'],
'billing_address' => $charge['metadata']['address'],
'billing_address_number' => $charge['metadata']['address_num'],
'billing_postal_code' => $charge['metadata']['postal_code'],
'billing_city' => $charge['metadata']['city'],
'billing_phone' => strval($charge['billing_details']['phone']),
'order' => json_decode($charge['metadata']['order']),
'total_quantity' => (int) $charge['metadata']['total_quantity'],
'billing_total' => $charge['amount'],
]);
}
This is all going well. However, I do not know how to notify the customer (on the frontend) that the order has been completed. In Stripe's documentation, they explain how to retrieve the Source on the order confirmation page, but they do not explain how to retrieve the Charge, because this is what determines whether the whole order has been completed or not.
OrderConfirmation.vue:
checkPaymentStatus() {
this.stripe = Stripe(this.stripeKey)
// After some amount of time, we should stop trying to resolve the order synchronously:
const MAX_POLL_COUNT = 10;
let pollCount = 0;
let params = new URLSearchParams(location.search)
const pollForSourceStatus = async () => {
const { source } = await this.stripe.retrieveSource({id: params.get('source'), client_secret: params.get('client_secret')})
if (source.status === 'chargeable') {
// Make a request to your server to charge the Source.
// Depending on the Charge status, show your customer the relevant message.
} else if (source.status === 'pending' && pollCount < MAX_POLL_COUNT) {
// Try again in a second, if the Source is still `pending`:
pollCount += 1;
setTimeout(pollForSourceStatus, 1000);
} else {
// Depending on the Source status, show your customer the relevant message.
}
};
pollForSourceStatus();
}
How do I go from here? I am trying to notify the frontend when the Charge has been succeeded. My initial thought process was just to return the Order object, as I would do if it was a Controller, but if I understand correctly, the Job is running asynchronously, so I can't return data. I am also new to Jobs and Queues and stuff, I'm still trying to wrap my head around with it.
Another option I thought of is that I would poll requests from the frontend to the backend to request the last Order, but I have no idea how this would work and/or if this is a good solution.
Any help/tips/helpful resources would be much appreciated!
iDEAL payments are asynchronous, but they luckily do immediately notify you if the payment was successful or not.
When the iDEAL process is complete and your user is redirected to your site, Stripe automatically appends some query parameters to the URL. Meaning your users will be redirected to something like:
https://example.com/checkout/complete?payment_intent=pi_123&payment_intent_client_secret=pi_123_secret_456&source_type=ideal
The next step is to then retrieve the PaymentIntent and check on its status, which you can do by either:
Retrieving the PaymentIntent on the client using stripe.js and the PaymentIntent client secret: https://stripe.com/docs/js/payment_intents/retrieve_payment_intent
Retrieving the PaymentIntent on the server by sending an ajax request to your backend with the PaymentIntend ID: https://stripe.com/docs/api/payment_intents/retrieve
If the status is succeeded, then the payment was completed and you can proceed from there.

Braintree Drop-In (cards & paypal) + subscriptions (PHP). Problem with paymentMethodNonce

I have been able to create a test check-out (js+php) in my server using the Braintree drop-in method. I need to create subscriptions for my customers accepting paypal or credit cards. The checkout (client-side) form is working and I POST it to a php page where I get the form data and create the Customer, plan, etc.
However, I have problems when creating the subscription. I get the Cannot use a paymentMethodNonce more than once or similar errors.
This is what I do:
checkout form (drop-in) submits data to subscription.php?payload_nonce=tokencc_bc_s27k55_c7pcq8_8mvd43_xxcvvf_9p5
braintree.dropin.create({
authorization: 'sandbox_6vqkmmsy_fs5432wttyb8qpzf',
container: '#dropin-container',
locale: 'es_ES',
paypal: {
flow: 'vault'
}
}, function (createErr, instance) {
button.addEventListener('click', function () {
instance.requestPaymentMethod(function (err, payload) {
// Submit payload.nonce to your server
url = 'subscription.html?payload_nonce=' + payload.nonce;
window.location.replace(url);
});
});
});
Then in the destination page I create the Customer...
$payload_nonce = $_GET['payload_nonce'];
$result = Braintree_Customer::create(array(
'firstName' => $name,
'email' => $email,
'company' => $company
));
...his payment method...
$resultPM = Braintree_PaymentMethod::create([
'customerId' => $customerId,
'paymentMethodNonce' => $payload_nonce
]);
...and finally the subscription...
$subscriptionData = array(
'paymentMethodNonce' => $payload_nonce,
'planId' => $billingplan
);
$result = Braintree_Subscription::create($subscriptionData);
I have tried to add the paymentMethodNonce when creating the Customer, or when creating the paymentMethod, etc. with same results.
If the payment method is "credit card" I can get the mayment method token like this:
'paymentMethodToken' => $result->customer->creditCards[0]->token
But if it is PayPal there is no creditcards[0]. Which is the correct method to achieve what I need?
Thanks!

PHP - Retrieve existing stripe customer and their cards?

I'm successfully able to create a new customer in Stripe via my PHP backend. That being said, when my user wants to view their existing cards, the PHP below just creates another new user and ephemeral key. If I already have a customer ID, how can I change my below code to make it so that if I'm providing the customer ID, existing cards are returned? Help is greatly appreciated!
if (!isset($_POST['api_version']))
{
header('HTTP/1.1 400 Bad Request');
}
//USER DETAILS
$email = $_POST['email'];
$customer = \Stripe\Customer::create(array(
'email' => $email,
));
try {
$key = \Stripe\EphemeralKey::create(
["customer" => $customer->id],
["stripe_version" => $_POST['api_version']]
);
header('Content-Type: application/json');
exit(json_encode($key));
} catch (Exception $e) {
header('HTTP/1.1 500 Internal Server Error');
}
You need to store the customer id you get back from stripe the first time you create a customer and then you can retrieve the user with:
\Stripe\Customer::retrieve($customer_id);
Something like this:
$customer_id = $_POST['customer_id']; //get this id from somewhere a database table, post parameter, etc.
// if the customer id doesn't exist create the customer
if ($customer_id !== null) {
// create the customer as you are now
} else {
$cards = \Stripe\Customer::retrieve($customer_id)->sources->all()
// return the cards
}

stripe api checking for existing card

I'm sure I'm missing something obvious here, but I can't get my head around how to check for an existing card against a customer.
I'm using the stripe connect api within an laravel app to manage payments on behalf of others, and the basic process is as follows:
a stripe token is created via stripe.js and submitted with the payment form
if the customer exists in the local database, I grab their stripe_id, otherwise a new customer is created using the token as the source/card
a charge is then created using the retrieved or new customer stripe_id
Currently, if the customer returns and uses a different card, as the charge only includes a customer, not source, it'll be charged against their default card regardless.
What I'd like to do is:
create a stripe token
check customer against local database etc
check card fingerprint against customer's cards
if necessary, create new card on customer's record
create charge using both customer and card ids
Simply put: I can't see where in the process a persistent card_id is generated; both those used in the stripe.js response, and when created in the stripe dashboard, appear to be unique, meaning every charge creates a brand-new card object in stripe.
I know I can retrieve a list of cards stored against a customer's account - but where do I get the initial card_id from to search against?
I've seen a question that touches on this here - Can I check whether stripe a card is already existed before going to create new one? - but I don't know Ruby, so can't make head nor tail of it.
EDIT:
Simpler version - is there a way to get the fingerprint as described in the stripe docs here - https://stripe.com/docs/api/php#card_object - without having to first create a card object ?
So the idea here would be to use the fingerprint on the Card object or the Token object and not the id itself as those would be different if you add the same card multiple times.
When you get a new card token you can retrieve it through the Retrieve Token API and look for the fingerprint in the card hash.
You would keep a list of known fingerprints in your database associated with a specific customer and/or card so that you can detect duplicate cards.
NOTE: make sure you are using the secret keys to get those information. otherwise if you are using the publishable key, you might not get the fingerprint value.
I created a function to do this:
$customer is the stripe customer object
$stripe_account is either your account's stripe ID or the connected account's stripe ID
$token comes from stripe.js elements
$check_exp allows you to decide if you want to check the card's expiration date as well, because the fingerprint does not change if the card's number is the same
stripe PHP API 7.0.0
function check_duplicate_card($customer, $stripe_account, $token, $check_exp) {
$loc = "check_duplicate_card >> ";
$debug = true;
if ($debug) {
// see here for an explanation for logging: http://php.net/set_error_handler >> Examples
trigger_error("$loc started", E_USER_NOTICE);
}
try
{
// get token data
$response = \Stripe\Token::retrieve(
$token,
["stripe_account" => $stripe_account]
);
$token_fingerprint = $response->card->fingerprint;
$token_exp_month = $response->card->exp_month;
$token_exp_year = $response->card->exp_year;
if ($debug) {
trigger_error("$loc token_fingerprint = $token_fingerprint; token_exp_month = $token_exp_month; token_exp_year = $token_exp_year", E_USER_NOTICE);
}
// check for duplicate source
if ($debug) {
trigger_error("$loc customer sources = " . json_encode($customer->sources), E_USER_NOTICE);
}
$duplicate_found = false;
foreach ($customer->sources->data as &$value) {
// get data
$fingerprint = $value->fingerprint;
$exp_month = $value->exp_month;
$exp_year = $value->exp_year;
if ($fingerprint == $token_fingerprint) {
if ($check_exp) {
if (($exp_month == $token_exp_month) && ($exp_year == $token_exp_year)) {
$duplicate_found = true;
break;
}
} else {
$duplicate_found = true;
break;
}
}
}
if ($debug) {
trigger_error("$loc duplicate_found = " . json_encode($duplicate_found), E_USER_NOTICE);
}
} catch (Exception $e) {
if ($e instanceof \Stripe\Exception\ApiErrorException) {
$return_array = [
"status" => $e->getHttpStatus(),
"type" => $e->getError()->type,
"code" => $e->getError()->code,
"param" => $e->getError()->param,
"message" => $e->getError()->message,
];
$return_str = json_encode($return_array);
trigger_error("$loc $return_str", E_USER_WARNING);
http_response_code($e->getHttpStatus());
echo $return_str;
} else {
$return_array = [
"message" => $e->getMessage(),
];
$return_str = json_encode($return_array);
trigger_error("$loc $return_str", E_USER_ERROR);
http_response_code(500); // Internal Server Error
echo $return_str;
}
}
if ($debug) {
trigger_error("$loc ended", E_USER_NOTICE);
}
return $duplicate_found;
}

Paymill subscription in PHP

I am trying to create a subscription i Paymill. Ive read through their examples but i do not seem to get it.
All i really want to do is to setup a subscription, here is my current code:
$token = $_POST['paymillToken'];
if ($token) {
require "Services/Paymill/Payments.php";
require "Services/Paymill/Transactions.php";
require "Services/Paymill/Subscriptions.php";
require "Services/Paymill/Offers.php";
$params = array(
'amount' => '49900', // Cent!
'currency' => 'SEK', // ISO 4217
'token' => $token,
'description' => 'User ID# ' . $userIdMain . ' Email: ' . $userEmail
);
$transactionsObject = new Services_Paymill_Transactions(
PAYMILL_API_KEY, PAYMILL_API_HOST
);
$transaction = $transactionsObject->create($params);
echo "<br /><br />";
print_r($transaction);
echo $transaction['client']['id'];
echo $transaction['payment']['id'];
$params = array(
'client' => $transaction['client']['id'],
'offer' => 'offer_9cdffb501f565bf827a8',
'payment' => $transaction['payment']['id']
);
$subscriptionsObject = new Services_Paymill_Subscriptions(PAYMILL_API_KEY, PAYMILL_API_HOST);
$subscription = $subscriptionsObject->create($params);
echo "<br /><br />";
print_r($subscription);
}
The problem is that the above create two payments at once. But it seems like the subscription object requires me to first have a payment_id (see $transaction['payment']['id'] above).
What am i doing wrong here?
Creating a subscription will also create a transaction, which is correct. Add a Payment Object ( https://www.paymill.com/en-gb/documentation-3/reference/api-reference/#create-new-credit-card-payment-with ) instead of creating a new transaction and pass the id to the Subscription->create().
OK it's not PHP but I had the same kind of uncertainty following the right steps to set up a subscription. In the end it's kind of easy, so I am posting my code so far in case it helps.
The app is MVC .net and I am using the PayMill PayButton to generate a payment token. This is part of the model during the registration process. The following code calls the .net wrapper of the PayMill API.
private static async Task<Subscription> MakePayMillSubscription(int amount, ApplicationUser user, PaymillContext paymillContext, Payment payment, Client client)
{
SubscriptionService subscriptionService = paymillContext.SubscriptionService;
Subscription subscription = await subscriptionService.CreateAsync( payment, client, null, amount, "EUR", Interval.periodWithChargeDay(1, Interval.TypeUnit.MONTH), null, user.Id, Interval.period(10, Interval.TypeUnit.YEAR));
return subscription;
}
private static async Task<Client> MakePayMillClient(RegisterViewModel model, ApplicationUser user, PaymillContext paymillContext)
{
ClientService clientService = paymillContext.ClientService;
Client client = await clientService.CreateWithEmailAndDescriptionAsync(
model.Email,
user.Id
);
return client;
}
private static async Task<Payment> MakePayMillPayment(RegisterViewModel model,PaymillContext context, Client client)
{
Payment payment = await context.PaymentService.CreateWithTokenAndClientAsync(model.paymillToken, client);
return payment;
}
Then I call these during registration like this at the moment (I am still in a testing phase)
client = await MakePayMillClient(model, user, paymillContext);
payment = await MakePayMillPayment(model, paymillContext,client);
subscription = await MakePayMillSubscription((int)(account.TotalAmountDue * 100.0M), user, paymillContext, payment, client);

Categories