I am running Active Collab 5.8.7 with PHP 5.6. I am using the API to create an invoice. I am following the API per this documentation. My issue is that the post requires an invoice number and I have no way of telling what the next invoice number should be.
I would like to know if there is a way to use the API to get the next invoice number inline or to add an invoice and let the system pick an invoice number for you.
Currently, I am using a random sequence of characters when I create an invoice through the API and then when we try to manually add an invoice, the invoice number field is blank. There has to be an easier and more consistent way to handle invoice numbers.
Thanks,
Larry
I figured this out on my own and wanted to post it here in case someone else needs it:
//get next invoice number
function get_next_ac_invoice_number($client) {
//get all invoices
$result = $client->get('/reports/run?type=InvoicesFilter')->getJson();
$invoices = $result['all']['invoices'];
usort($invoices,'sortbyInvoiceId');
$next_invoice_id = strval(intval(explode('-',$invoices[0]['number'])[0]) + 1) . '-' . date("Y");
return $next_invoice_id;
}
function sortbyInvoiceId($a,$b){
if ($a == $b) {
return 0;
}
return ($b < $a) ? -1 : 1;
}
EDIT FEB 22 '17
There is a bunch of cruft in here for Braintree but you should be able to get the gist of it. One thing I would make sure you consider is that you clear out AC's trash before posting since the next invoice id function will not return trashed invoices and will cause a duplicate invoice id error.
//get next invoice number
function get_next_ac_invoice_number($client) {
#get all invoices
$trashit = $client->delete('/trash');
$result = $client->get('/reports/run?type=InvoicesFilter')->getJson();
$invoices = $result['all']['invoices'];
usort($invoices,'sortbyInvoiceId');
$next_invoice_id = strval(intval(explode('-',$invoices[0]['number'])[0]) + 1) . '-' . date("Y");
return $next_invoice_id;
}
//creates an invoice in active collab
function create_ac_invoice($customer, $subscription, $transaction, $client) {
//get the next invoice ID
get_next_ac_invoice_number($client);
$plans = Braintree_Plan::all();
$plan;
foreach ($plans AS $myplan) {
if (strtolower($myplan->id) == strtolower($subscription->planId)) {
$plan = $myplan;
}
}
if (isset($transaction->discounts[0])) {
$result = $client->post('invoices', [
'company_id' => $customer['company_id'],
'number' => get_next_ac_invoice_number($client),
'items' => [
[
'description' => $plan->name . " - " . $plan->description,
'quantity' => 1,
'unit_cost' => $subscription->price
],
[
'description' => 'Promo Code Discount - ' . $transaction->discounts[0]->name,
'quantity' => 1,
'unit_cost' => (float) $transaction->discounts[0]->amount * -1
]
],
'private_note' => 'Auto-generated by Braintree'
]);
} else {
$result = $client->post('invoices', [
'company_id' => $customer['company_id'],
'number' => get_next_ac_invoice_number($client),
'items' => [
[
'description' => $plan->name . " - " . $plan->description,
'quantity' => 1,
'unit_cost' => $subscription->price
]
],
'private_note' => 'Auto-generated by Braintree'
]);
}
$invoice = $result->getJson();
if (isset($invoice['message'])) {
//we have an error, let's log and send email
$dump = print_r($invoice, true) . print_r($customer, true) . print_r($subscription, true);
logit('ERROR', $dump);
sendEmail($dump, 'Braintree Webhook Error Creating AC Invoice');
}
//mark the invoice as paid
$result = $client->post('payments', [
'parent_type' => 'Invoice',
'parent_id' => $invoice['single']['id'],
'amount' => getTotalCost($subscription, $transaction),
'comment' => 'Paid in full'
]);
$result = $result->getJson();
if (isset($result['message'])) {
//we have an error, let's log and send email
$dump = print_r($invoice, true) . print_r($customer, true) . print_r($subscription, true);
logit('ERROR', $dump);
sendEmail($dump, 'Braintree Webhook Error Creating AC Payment');
}
//send the invoice
$result = $client->put('invoices/' . $invoice['single']['id'] . '/send', [
'recipients' => [
$customer['email']
],
'subject' => "New Invoice",
'message' => "Thanks!",
'allow_payments' => 2
]);
$result = $result->getJson();
if (isset($result['message'])) {
//we have an error, let's log and send email
$dump = print_r($invoice, true) . print_r($customer, true) . print_r($subscription, true);
logit('ERROR', $dump);
sendEmail($dump, 'Braintree Webhook Error Sending AC Invoice Email');
}
return $invoice;
}
Related
How to retrieve the identifier of an existing customer via his email address or create the customer if he does not hesitate when creating a payment with API Stripe?
I searched in the Stripe documentation but couldn't find the answer.
require "Stripe/vendor/autoload.php";
// This is your test secret API key.
\Stripe\Stripe::setApiKey("sk_test_XXX");
header("Content-Type: application/json");
try {
// retrieve JSON from POST body
$jsonStr = file_get_contents("php://input");
$jsonObj = json_decode($jsonStr);
// get customer if exist
$query = \Stripe\Customer::search([
"query" => 'email:\'.'.$user['email'].'.\'',
]);
if ($query->id) {
$customer_ID = $query->id;
} else {
$customer = \Stripe\Customer::create([
"email" => $user["email"],
"description" => 'VIP plan',
]);
$customer_ID = $customer->id;
}
// Create a PaymentIntent with amount and currency
$paymentIntent = \Stripe\PaymentIntent::create([
"customer" => $customer_ID,
"amount" => 1400,
"currency" => "usd",
"automatic_payment_methods" => [
"enabled" => true,
],
]);
$output = [
"clientSecret" => $paymentIntent->client_secret,
];
echo json_encode($output);
} catch (Error $e) {
http_response_code(500);
echo json_encode(["error" => $e->getMessage()]);
}
Your search query is not a simple object but a multidimentional object.
'data' is missing on your object request :
$query->data[0]->id
You can't have access to datas so you might use a for loop as :
if(sizeof($query->data) !== 0)
{
for($i=0;$i<sizeof($query->data);$i++)
{
$customer_ID = $query->data[$i]->id;
}
}
else
{
// Create customer
}
If you're sure to have only one customer, you need to add a limit to the Stripe search query and so, you'll don't need to have for loop :
$query = \Stripe\Customer::search([
"query" => 'email:\'.'.$user['email'].'.\'',
"limit" => 1,
]);
if(sizeof($query->data) !== 0)
{
$customer_ID = $query->data[0]->id;
}
else
{
// create customer
}
Query fields for customers source: https://stripe.com/docs/search#query-fields-for-customers
FIELD USAGE TYPE (TOKEN, STRING, NUMERIC)
------- ---------------------- -----------------------------
created created>1620310503 numeric
email email~"amyt" string
metadata metadata["key"]:"value" token
name name~"amy" string
phone phone:"+19999999999" string
Here is the properly formatted query.
"query" => 'email~"'.$user['email'].'"', "limit" => 1
Recently i'm working on a laravel project which i choose to use srmklive/laravel-paypal as my paypal payment gateway plugin, but i found that i cannot add Tax and shipping fees, i have read the readme.doc multiple time and also check the paypal documentation, but i still confused on how to add the tax and shipping fees.
Did any one know how to add these data when setExpresssCheckout?
here is my code:
$provider = new ExpressCheckout;
$provider = PayPal::setProvider('express_checkout');
$itemsArr = $data = [];
// add items
foreach ($order->orderProduct as $order_product) {
array_push($itemsArr, [
'name' => $order_product->product->name,
'price' => $order_product->price,
'qty' => $order_product->quantity
]);
}
// add shipping fees
array_push($itemsArr, [
'name' => "Shipping Method : ".$order->shipping_method,
'price' => $order->shipping_fees,
'qty' => 1
]);
$data["items"] = $itemsArr;
$data['invoice_id'] = 1;
$data['invoice_description'] = "Order #1 Invoice";
$data['return_url'] = url('/payment/success');
$data['cancel_url'] = url('/cart');
$total = 0;
foreach($data['items'] as $item) {
$total += $item['price']*$item['qty'];
}
$data['total'] = $total;
$options = [
'BRANDNAME' => 'Ulife',
'LOGOIMG' => asset("images\ulifelanding.png"),
'CHANNELTYPE' => 'Merchant'
];
$response = $provider->setCurrency('MYR')->addOptions($options)->setExpressCheckout($data);
Ps: i also asked on the github, but still confused.
I don't know who still needs an answer for this question, but I share ("srmklive/paypal": "~1.0").
go to ExpressCheckout.php and go to setExpressCheckout add this :
'PAYMENTREQUEST_0_AMT' => $data['total']+$data['tax'],
'PAYMENTREQUEST_0_TAXAMT' => $data['tax'],
and you checkoutData put now:
'tax'=>xx
call your provider with addOptions:
example:
$options = [
'BRANDNAME' => 'MyBrand',
'LOGOIMG' => 'https://example.com/mylogo.png',
'CHANNELTYPE' => 'Merchant',
];
$provider->addOptions($options)->setExpressCheckout($checkoutData)
My website currently has a weird bug that only shows itself on the live version, but not on my local setup.
Essentially, in this scenario, a user is trying to ask an artist (another type of user) for a quote, hence the requestQuote function.
public function requestQuote(Request $request, AppMailer $mailer) {
$reciUsername = $request->input('recipient');
$recipientID = User::where('username', $reciUsername)->value('id');
$recipient = User::where('username', $reciUsername)->first();
$active_commissions_count = Commission::where('artist_id', $recipientID)
->where('complete', 0)
->where('stripe_charge_id', '!=', "")
->where('stripe_charge_id', '!=', null)
->count();
$this->validate($request, [
'message' => 'required|max:5000|min:10',
]);
$message = Auth::user()->messages()->create([
'body' => $request->input('message'),
'subject' => 'Status: Commission requested!',
'recipient_id' => $recipientID,
'sender_read' => 1,
'is_quote' => 1,
'quote_expires' => Carbon\Carbon::now()->addWeeks(1),
]);
$from = Auth::user()->username;
$mailer->sendQuoteRequestEmailTo($recipient, $from);
Auth::user()->update([
'timeout' => Carbon\Carbon::now(),
]);
dd("Message ID: " . $message->id . " - Message User ID: " . $message->user_id);
return redirect()->back()->with('info', 'Quote sent.');
}
This creates a message which the artist can then read. In this scenario, the recipient_id user is the artist, and the user_id user is the one who made the original quote. This is not supposed to change at any point during this transaction.
You'll notice there's a diedump at the end there.
In this scenario, the dd returns
"QUOTE REQUEST - Message ID: 30 - Message User ID: 2 - Recipient ID: 21"
Great. Everything works well so far, and the correct message is sent through, with the correct user being shown as having sent the quote.
The problem happens when the artist replies to the quote.
public function quoteReply(Request $request, $messageId, AppMailer $mailer) {
$this->validate($request, [
"reply-{$messageId}" => 'max:5000',
"price" => 'required|min:10|max:9999|numeric',
"date" => 'required|date',
], [
'max' => 'The text body has a maximum of 5000 characters.',
'price.max' => 'Commissions have a maximum of 9999 US dollars.',
'price.min' => 'Commissions have a minimum of 10 US dollars.',
]);
$message = Message::notReply()->find($messageId);
if (!$message) {
return redirect()->route('home')->with('info', 'Something went wrong.');
}
$date = $request->input('date');
$date2 = DateTime::createFromFormat('m/d/Y', $date);
$date3 = $date2->format("Y-m-d");
$reply = message::create([
'body' => $request->input("reply-{$messageId}"),
'sender_read' => 3,
'reci_read' => 3,
])->user()->associate(Auth::user());
Message::where('id', $messageId)->update([
'updated_at' => Carbon\Carbon::now(),
'subject' => 'Status: Price quote delivered.',
'quoted' => 1,
'price' => $request->input('price'),
'estimated_date' => $date3,
'quote_expires' => Carbon\Carbon::now()->addWeeks(1),
]);
$userID = $message->value('user_id');
$recipientID = $message->value('recipient_id');
$user = User::where('id', $userID)->first();
if (Auth::user()->id == $userID) {
Message::where('id', $messageId)->update(['reci_read' => 0]);
}
if (Auth::user()->id == $recipientID) {
Message::where('id', $messageId)->update(['sender_read' => 0]);
}
$message->replies()->save($reply);
$from = Auth::user()->username;
$mailer->sendQuoteReplyEmailTo($user, $from);
dd("QUOTE REPLY - Message ID: " . $message->id . " - Message User ID: " . $message->user_id . " - Message Recipient ID: " . $message->recipient_id . " - User ID: " . $user->id );
return redirect()->back()->with('info', 'Price quote delivered.');
}
This will return:
"QUOTE REPLY - Message ID: 30 - Message User ID: 2 - Message Recipient
ID: 21 - User ID: 3"
I first noticed this problem when the wrong user was receiving email. In fact it's ALWAYS the same user getting the email. User 3. If I use a different user for the artist, and a different user for the person requesting the quote, $user always becomes user 3.
Despite this, the correct users are still getting correct messages. At the moment this is only affecting who's getting the correct email in the quoteReply function.
What could be causing this?
Switching
$userID = $message->value('user_id');
to
$userID = $message->user_id;
Fixed my problem.
I'm working with the open source ticket system called Attendize.
They already have the payment provider Stripe integrated. Now I'm trying to make this work with the payment provider Mollie.
The problem is I keep stumbling on this error:
My code looks like this:
$transaction_data += [
'transactionId' => $event_id . date('YmdHis'),
'returnUrl' => route('showEventCheckoutPaymentReturn', [
'event_id' => $event_id,
'is_payment_successful' => 1
]),
];
$apiKey = "test_gSDS4xNA96AfNmmdwB3fAA47******";
$gateway->setApiKey($apiKey);
$transaction = $gateway->purchase($transaction_data);
$response = $transaction->send();
if ($response->isSuccessful()) {
session()->push('ticket_order_' . $event_id . '.transaction_id',
$response->getTransactionReference());
return $this->completeOrder($event_id);
} elseif ($response->isRedirect()) {
/*
* As we're going off-site for payment we need to store some data in a session so it's available
* when we return
*/
session()->push('ticket_order_' . $event_id . '.transaction_data', $transaction_data);
Log::info("Redirect url: " . $response->getRedirectUrl());
$return = [
'status' => 'success',
'redirectUrl' => $response->getRedirectUrl(),
'message' => 'Redirecting to ' . $ticket_order['payment_gateway']->provider_name
];
// GET method requests should not have redirectData on the JSON return string
if($response->getRedirectMethod() == 'POST') {
$return['redirectData'] = $response->getRedirectData();
}
return response()->json($return);
} else {
// display error to customer
return response()->json([
'status' => 'error',
'message' => $response->getMessage(),
]);
}
When I debug my code he's going into the elseif ($response->isRedirect()) {. I am being redirected to Mollie and a can do a successful payment. But when I am being redirect back to http://myurl.dev/e/1/checkout/success?is_payment_successful=1 I'm getting the error.
UPDATE:
In my return function I have the following code:
public function showEventCheckoutPaymentReturn(Request $request, $event_id)
{
if ($request->get('is_payment_cancelled') == '1') {
session()->flash('message', 'You cancelled your payment. You may try again.');
return response()->redirectToRoute('showEventCheckout', [
'event_id' => $event_id,
'is_payment_cancelled' => 1,
]);
}
$ticket_order = session()->get('ticket_order_' . $event_id);
$gateway = Omnipay::create($ticket_order['payment_gateway']->name);
$gateway->initialize($ticket_order['account_payment_gateway']->config + [
'testMode' => config('attendize.enable_test_payments'),
]);
$transaction = $gateway->completePurchase($ticket_order['transaction_data'][0]);
$response = $transaction->send();
if ($response->isSuccessful()) {
session()->push('ticket_order_' . $event_id . '.transaction_id', $response->getTransactionReference());
return $this->completeOrder($event_id, false);
} else {
session()->flash('message', $response->getMessage());
return response()->redirectToRoute('showEventCheckout', [
'event_id' => $event_id,
'is_payment_failed' => 1,
]);
}
}
The problem (error) is with $response = $transaction->send();.
The array $ticket_order['transaction_data'][0] contains this:
Array
(
[amount] => 80
[currency] => EUR
[description] => Order for customer: niels#email.be
[transactionId] => 120170529082422
[returnUrl] => http://eventy.dev/e/1/checkout/success?is_payment_successful=1
)
UPDATE 2:
I've added $gateway->setApiKey($apiKey); in my return function. But the problem is that my response is NOT successful. So he doesn't go into $response->isSuccessful(). When I dump my $response variable just before he checks if it's successful it shows this: https://pastebin.com/NKCsxJ7B.
You can see there's an error like this:
[error] => Array
(
[type] => request
[message] => The payment id is invalid
)
The payment in Mollie looks like this:
UPDATE 3:
In my return function I tried to check the status of the response object like this : $response->status(). This gave me the following error:
Call to undefined method Omnipay\Mollie\Message\CompletePurchaseResponse::status()
Then I tried $response->getStatus() but this gave me nothing back.
What #Daan said in his comment is correct, you are getting the error from the landing page, not the page that creates the transaction.
On that landing page you will have a call like this:
$omnipay->completePurchase($data);
In that #data array you need to include the 'transactionReference' field which should be one of the POST parameters that your http://myurl.dev/e/1/checkout/success?is_payment_successful=1 URL received.
Probably a useful debugging aid is to have the code at that URL print out or log the entire $_POST array and you can use that to check what parameter you need to pull from that array. It varies a bit between gateways.
This might have something to do with this ticket: https://github.com/thephpleague/omnipay-eway/issues/13
To solve this check I would suggest checking status code with
if ($request->status() == 201) {
//successful created
}
My theory is that it is checking against 200
The function is defined here:
https://github.com/thephpleague/omnipay-mollie/blob/master/src/Message/AbstractResponse.php
public function isSuccessful()
{
return !$this->isRedirect() && !isset($this->data['error']);
}
It will probably fail because you expect a redirect!
201 because of my Postman test below
Why i am getting the following
error
Warning: SoapClient::__doRequest(): Unable to find the wrapper "https"
- did you forget to enable it when you configured PHP
What is my mistake?
My code is following
$config = parse_ini_file('ebay.ini', true);
$site = $config['settings']['site'];
$compatibilityLevel = $config['settings']['compatibilityLevel'];
$dev = $config[$site]['devId'];
$app = $config[$site]['appId'];
$cert = $config[$site]['cert'];
$token = $config[$site]['authToken'];
$location = $config[$site]['gatewaySOAP'];
// Create and configure session
$session = new eBaySession($dev, $app, $cert);
$session->token = $token;
$session->site = 203; // 0 = US;
$session->location = $location;
// Make an AddItem API call and print Listing Fee and ItemID
try {
$client = new eBaySOAP($session);
$PrimaryCategory = array('CategoryID' => 357);
$Item = array('ListingType' => 'Chinese',
'Currency' => 'INR',
'Country' => 'US',
'PaymentMethods' => 'PaymentSeeDescription',
'RegionID' => 0,
'ListingDuration' => 'Days_3',
'Title' => 'The new item',
'Description' => "It's a great new item",
'Location' => "San Jose, CA",
'Quantity' => 1,
'StartPrice' => 24.99,
'PrimaryCategory' => $PrimaryCategory,
);
$params = array('Version' => $compatibilityLevel, 'Item' => $Item);
$results = $client->AddItem($params);
// The $results->Fees['ListingFee'] syntax is a result of SOAP classmapping
print "Listing fee is: " . $results->Fees['ListingFee'] . " <br> \n";
print "Listed Item ID: " . $results->ItemID . " <br> \n";
print "Item was listed for the user associated with the auth token code herer>\n";`enter code here`
} catch (SOAPFault $f) {
print $f; // error handling
}
Thanks in advance
Murali
You have to add (or uncomment it) extension=php_openssl.dll; to your php.ini file.