cakephp and Webhooks - php

Im new to cakephp and have implemented the Webshop Solution Snipcart. When an order is processed by snipcart they send a webhook to our site. The webhook looks like this according to their documentation:
{
eventName: "order:completed",
mode: "Live",
createdOn: "2013-07-04T04:18:44.5538768Z",
content: {
token: "22808196-0eff-4a6e-b136-3e4d628b3cf5",
creationDate: "2013-07-03T19:08:28.993Z",
modificationDate: "2013-07-04T04:18:42.73Z",
status: "Processed",
paymentMethod: "CreditCard",
email: "customer#snipcart.com",
cardHolderName: "Nicolas Cage",
billingAddressName: "Nicolas Cage",
billingAddressCompanyName: "Company name",
billingAddressAddress1: "888 The street",
billingAddressAddress2: "",
billingAddressCity: "Québec",
billingAddressCountry: "CA",
billingAddressProvince: "QC",
billingAddressPostalCode: "G1G 1G1",
billingAddressPhone: "(888) 888-8888",
shippingAddressName: "Nicolas Cage",
shippingAddressCompanyName: "Company name",
shippingAddressAddress1: "888 The street",
shippingAddressAddress2: "",
shippingAddressCity: "Québec",
shippingAddressCountry: "CA",
shippingAddressProvince: "QC",
shippingAddressPostalCode: "G1G 1G1",
shippingAddressPhone: "(888) 888-8888",
shippingAddressSameAsBilling: true,
finalGrandTotal: 310.00,
shippingAddressComplete: true,
creditCardLast4Digits: "4242",
shippingFees: 10.00,
shippingMethod: "Livraison",
items: [{
uniqueId: "eb4c9dae-e725-4dad-b7ae-a5e48097c831",
token: "22808196-0eff-4a6e-b136-3e4d628b3cf5",
id: "1",
name: "Movie",
price: 300.00,
originalPrice: 300.00,
quantity: 1,
url: "https://snipcart.com",
weight: 10.00,
description: "Something",
image: "http://placecage.com/50/50",
customFieldsJson: "[]",
stackable: true,
maxQuantity: null,
totalPrice: 300.0000,
totalWeight: 10.00
}],
subtotal: 610.0000,
totalWeight: 20.00,
hasPromocode: false,
promocodes: [],
willBePaidLater: false
}
}
And the consume the webhooks looks like this:
<?php
$json = file_get_contents('php://input');
$body = json_decode($json, true);
if (is_null($body) or !isset($body['eventName'])) {
// When something goes wrong, return an invalid status code
// such as 400 BadRequest.
header('HTTP/1.1 400 Bad Request');
return;
}
switch ($body['eventName']) {
case 'order:completed':
// This is an order:completed event
// do what needs to be done here.
break;
}
// Return a valid status code such as 200 OK.
header('HTTP/1.1 200 OK');
My question is, how do I do this in CakePHP version 2.4. I've been looking for days now on the internet for a solution, but tho i'm inexperienced I can't find a proper solution.
Solved it:
public function webhooks(){
//check if POST
if ($this->request->is('post')) {
//Allow raw POST's
$url = 'php://input';
//decode
$json = json_decode(file_get_contents($url), true);
if (is_null($json) or !isset($json['eventName'])) {
// When something goes wrong, return an invalid status code
// such as 400 BadRequest.
header('HTTP/1.1 400 Bad Request');
return;
}
//do whatever needs to be done, in this case remove the quantity ordered from the stock in db.
switch ($json['eventName']) {
case 'order:completed':
$id = $json['content']['items'][0]['id'];
$quantity = $json['content']['items'][0]['quantity'];
$query = $this->Shop->findById($id, 'Shop.stock');
$stock = $query['Shop']['stock'];
$stock = $stock - $quantity;
$this->Shop->updateAll(array('Shop.stock' => $stock), array('Shop.id' => $id));
break;
}
header('HTTP/1.1 200 OK');
}
}

the only thing I would add to your answer is a few more "cake" ways of doing some things.
public function webhooks(){
//check if POST
if ($this->request->is('post') || $this->request->is('put')) {
//Allow raw POST's
$url = 'php://input';
//decode
$json = json_decode(file_get_contents($url), true);
if (empty($json) or empty($json['eventName'])) {
throw new BadRequestException('Invalid JSON Information');
}
//do whatever needs to be done, in this case remove the quantity ordered from the stock in db.
switch ($json['eventName']) {
case 'order:completed':
$id = $json['content']['items'][0]['id'];
$quantity = $json['content']['items'][0]['quantity'];
$shop = $this->Shop->find('first', array(
'conditions' => array(
'Shop.id' => $id,
),
'fields' => array(
'Shop.stock',
),
));
if (empty($shop)) {
throw new NotFoundException(__('Invalid Shop Content'));
}
$stock = $shop['Shop']['stock'];
$stock = $stock - $quantity;
$this->Shop->id = $id;
$this->Shop->saveField('stock', $stock);
break;
}

Related

JSON update in PHP : duplication

I have a strange issue when I'm trying to update a json in my database.
This is my code :
// Update content of user cart
$contentJSON = json_decode($userCart['content'],true);
$newJSONContent;
$wineNotExist = true;
$index = 1;
foreach($contentJSON as $key) {
// Update content if wine is already in user cart
if ($key['wineId'] === $wineId) {
$contentObject->wineId = $wineId;
$contentObject->wineQuantity = $key['wineQuantity'] + $wineQuantity;
$dataIndex = strval($index);
$newJSONContent->$dataIndex = $contentObject;
$wineNotExist = false;
} else {
$contentObject->wineId = $key['wineId'];
$contentObject->wineQuantity = $key['wineQuantity'];
$dataIndex = strval($index);
$newJSONContent->$dataIndex = $contentObject;
}
$index++;
}
if ($wineNotExist) {
$dataKey = strval($index);
$contentObject->wineId = $wineId;
$contentObject->wineQuantity = $wineQuantity;
$newJSONContent->$dataKey = $contentObject;
}
So, I decided to build a new JSON and parse the former; moreover if json not contains the wineId we created a new data input in JSON but this section replaces previous key by the right one and create the right one as you can see in AJAX response here :
Response when it's the same wineId
{
"code": 200,
"status": "success",
"message": "This wine has been added to your cart",
"content": {
"1": {
"wineId": 2,
"wineQuantity": 3
}
},
"index": true
}
Response when wineId is not contains in cart
{
"code": 200,
"status": "success",
"message": "This wine has been added to your cart",
"content": {
"1": {
"wineId": 5,
"wineQuantity": 1
},
"2": {
"wineId": 5,
"wineQuantity": 1
}
},
"index": true
}
I don't understand why the instruction in if replaces previous value, do you have an idea why ?
When I use another variable name for contentObject in if instruction, the code works but why ? Because $contentObject is a local variable in foreach loop
Sorry for my english, I'm French.
It works with this code, so the problem is that $contentObject has the previous value whereas it's a local varibale, is it specific to PHP ?
// Update content of user cart
$contentJSON = json_decode($userCart['content'],true);
$newJSONContent;
$wineNotExist = true;
$index = 1;
foreach($contentJSON as $key) {
// Update content if wine is already in user cart
if ($key['wineId'] === $wineId) {
$contentObject=null;
$contentObject->wineId = $wineId;
$contentObject->wineQuantity = $key['wineQuantity'] + $wineQuantity;
$dataIndex = strval($index);
$newJSONContent->$dataIndex = $contentObject;
$wineNotExist = false;
} else {
$contentObject=null;
$contentObject->wineId = $key['wineId'];
$contentObject->wineQuantity = $key['wineQuantity'];
$dataIndex = strval($index);
$newJSONContent->$dataIndex = $contentObject;
}
$index++;
}
if ($wineNotExist) {
$contentObject=null;
$dataKey = strval($index);
$contentObject->wineId = $wineId;
$contentObject->wineQuantity = $wineQuantity;
$newJSONContent->$dataKey = $contentObject;
}

Google Content API for Shopping: Error Creating Shipment

I'm looking to mark an order in Google Shopping as shipped but getting hung up with how to format the lineItems. I'm following the official docs for shipping line items:
https://developers.google.com/shopping-content/v2/reference/v2.1/orders/shiplineitems
And here is my code:
$client = new Google_Client();
$service = new Google_Service_ShoppingContent($client);
// Get Order and Line Items
$order = $service->orders->get('MERCHANT-ID', 'ORDER-ID', array());
$lineItems = $order->getLineItems();
// Prepare Item Info
foreach($lineItems as $item) {
$items = array('lineItemId' => $item->getId(),'productId' => $item->getProduct()->getId(), 'quantity' => $item->quantityPending);
}
// Prepare Shipment Info
$shipment = array('shipmentId' => 'xxx', 'carrier' => 'ups', 'trackingId' => '1234567890');
// Prepare PostBody
$postBody = new Google_Service_ShoppingContent_OrdersShipLineItemsRequest();
$postBody->operationId = rand();
$postBody->lineItems = $items;
$postBody->shipmentInfos = $shipment;
// Mark Google Order as Shipped
$service->orders->shiplineitems('MERCHANT-ID', 'ORDER-ID', $postBody, array());
This produces the following error, but I haven't been able to figured out exactly what is wrong:
Google_Service_Exception: { "error": { "errors": [ { "domain": "global", "reason": "invalid", "message": "Invalid value for lineItems: {lineItemId=HI2PTRMVLNCZEXP, productId=online:en:US:d3k3245, quantity=2}", "locationType": "other", "location": "" } ], "code": 400, "message": "Invalid value for lineItems: {lineItemId=HI2PTRMVLNCZEXP, productId=online:en:US:d3k3245, quantity=2}" } }
Any ideas what I'm doing wrong?
The API is expecting an Array of Google_Service_ShoppingContent_OrderShipmentLineItemShipment class.
$lineItemShipments = array();
$lineItemShipment = new Google_Service_ShoppingContent_OrderShipmentLineItemShipment();
$lineItemShipment->setLineItemId($item->getId());
$lineItemShipment->setQuantity($item->quantityPending);
$lineItemShipments[] = $lineItemShipment;
$postBody->setLineItems($lineItemShipments);

How to set validation method in Google spreadsheets API

I am confused about the new Google Sheets API v4. My question is: how can I set validation rules for specified column(s) in spreadsheet?
There is no useful tutorial with description how to use appropriate methods.
Result should look like following sample:
That validation should be set before data upload (which works fine).
My current code:
$client = getClient();
$service = new Google_Service_Sheets($client);
$fileId = 'my-document-id';
$body = new Google_Service_Sheets_SetDataValidationRequest(array(
'setRange' => new Google_Service_Sheets_GridRange(
array(
'sheetId'=>'List1',
'startColumnIndex'=>'0',
'endColumnIndex'=>'1'
)
),
'setRule' => new Google_Service_Sheets_DataValidationRule(
array(
'setValues'=>array('YES','NO')
)
)
));
$sheetReq = new Google_Service_Sheets_Request($client);
$sheetReq->setSetDataValidation($body);
$batchUpdateRequest = new Google_Service_Sheets_BatchUpdateSpreadsheetRequest(array(
'requests' => $sheetReq
));
$result = $service->spreadsheets->batchUpdate($fileId, $batchUpdateRequest);
Thank you #random-parts for help, it has brought me to the right track. If someone else will try to solve similar problem in PHP in feature, please find bellow fully working example:
$client = $this->getClient();
$service = new Google_Service_Sheets($client);
$ary_values = ['yes','nope','maybe','never ever'];
foreach( $ary_values AS $d ) {
$cellData = new Google_Service_Sheets_ConditionValue();
$cellData->setUserEnteredValue($d);
$values[] = $cellData;
}
$conditions = new Google_Service_Sheets_BooleanCondition();
$conditions->setType('ONE_OF_LIST');
$conditions->setValues($values);
$setRule= new Google_Service_Sheets_DataValidationRule();
$setRule->setCondition($conditions);
$setRule->setInputMessage('Please set correct value');
$setRule->setShowCustomUi(true);
$range = new Google_Service_Sheets_GridRange();
$range->setStartRowIndex(1);
$range->setEndRowIndex(5);
$range->setStartColumnIndex(1);
$range->setEndColumnIndex(2);
$range->setSheetId(YOUR_SHEET_ID); //replace this by your sheet ID
$valReq = new Google_Service_Sheets_SetDataValidationRequest();
$valReq->setRule($setRule);
$valReq->setRange($range);
$sheetReq = new Google_Service_Sheets_Request();
$sheetReq->setSetDataValidation($valReq);
$bodyReq = new Google_Service_Sheets_BatchUpdateSpreadsheetRequest();
$bodyReq->setRequests($sheetReq);
$result = $service->spreadsheets->batchUpdate($fileId, $bodyReq);
The DataValidationRule object would look like the following:
"rule": {
"condition": {
"type": "ONE_OF_LIST",
"values": [
{ userEnteredValue: "Yes"},
{ userEnteredValue: "No"}
],
},
"inputMessage": "",
"strict": true,
"showCustomUi": true,
}
You want to use rule.condition.type ONE_OF_LIST and then enter the rule.condition.values you want in the list. showCustomUi will show the dropdown
A full example using google apps script from the Sheets script editor:
function setDataVal () {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheets()[0];
var validation = {
"setDataValidation": {
"range": {
"sheetId": sheet.getSheetId(),
"startRowIndex": 1,
"endRowIndex": 5,
"startColumnIndex": 1,
"endColumnIndex": 5,
},
"rule": {
"condition": {
"type": "ONE_OF_LIST",
"values": [
{ userEnteredValue: "Yes"},
{ userEnteredValue: "No"}
],
},
"inputMessage": "",
"strict": true,
"showCustomUi": true,
}
},
}
var req = {
"requests": [validation],
"includeSpreadsheetInResponse": false,
}
Sheets.Spreadsheets.batchUpdate(req, ss.getId())
}
Sheets API advanced service will have to be enabled

mysql to json using php. Nested objects

Good Afternoon,
I am trying to get these results into arrays in PHP so that I can encode them into json objects and send them to the client. The results of the query look like this:
id name hours cat status
3bf JFK Int 24 pass open
3bf JFK Int 24 std closed
3bf JFK Int 24 exp open
5t6 Ohm CA 18 pass closed
5t6 Ohm CA 18 std closed
5t6 Ohm CA 18 std2 open
5t6 Ohm CA 18 exp open
...
I would like for the json objects to look like this:
{ "id": "3bf", "name": "JFK Int", "cats":
{ [ { "cat": "pass", "status": "open" },
{ "cat": "std", "status": "closed" },
{ "cat": "exp", "status": "open" } ] }
{ "id": "5t6", "name": "Ohm CA", "cats":
{ [ { "cat": "pass", "status": "closed" },
{ "cat": "std", "status": "closed" },
{ "cat": "std2", "status": "open" } ],
{ "cat": "exp", "status": "open" } ] }
I have succesfully connected to mysql and exported using json_encode using flat tables but this part I do not know how to do in PHP. Thanks.
This is the code that I have. This returns an array of json objects but it is flat, not nested:
$SQL = "SELECT id, name, hours, cat, status FROM bwt.vewPortCats";
$result = mysql_query($SQL);
$arr = array();
while ($row = mysql_fetch_assoc($result)) {
$arr[] = $row;}
$json = json_encode($arr);
echo $json;
The data itself is from a view that combines the tables ports and cats.
what you could do (sorry, not the best code I could write... short on time, ideas, and energy ;-) is something like this (I hope it still conveys the point):
$SQL = "SELECT id, name, hours, cat, status FROM bwt.vewPortCats";
$result = mysql_query($SQL);
$arr = array();
while ($row = mysql_fetch_assoc($result)) {
// You're going to overwrite these at each iteration, but who cares ;-)
$arr[$row['id']]['id'] = $row['id'];
$arr[$row['id']]['name'] = $row['name'];
// You add the new category
$temp = array('cat' => $row['cat'], 'status' => $row['status']);
// New cat is ADDED
$arr[$row['id']]['cats'][] = $temp;
}
$base_out = array();
// Kind of dirty, but doesn't hurt much with low number of records
foreach ($arr as $key => $record) {
// IDs were necessary before, to keep track of ports (by id),
// but they bother json now, so we do...
$base_out[] = $record;
}
$json = json_encode($base_out);
echo $json;
Haven't had the time to test or think twice about it, but again, I hope it conveys the idea...
With thanks to #maraspin, I have got my below code:
function merchantWithProducts($id)
{
if (
!empty($id)
) {
//select all query
$query = "SELECT
m.id as 'mMerchantID', m.name as 'merchantName', m.mobile, m.address, m.city, m.province,
p.id as 'ProductID', p.merchantId as 'pMerchantID', p.category, p.productName, p.description, p.price, p.image, p.ratingCount
FROM " . $this->table_name . " m
JOIN by_product p
ON m.id = p.merchantId
WHERE m.id = :id
GROUP BY m.id";
// prepare query statement
$stmt = $this->conn->prepare($query);
// sanitize
// $this->id = htmlspecialchars(strip_tags($this->id));
// bind values
$stmt->bindParam(":id", $this->id);
try {
$success = $stmt->execute();
if ($success === true) {
$results = $stmt->fetchAll();
$this->resultToEncode = array();
foreach ($results as $row) {
$objItemArray = array(
"merchantID" => $row->mMerchantID,
"merchantName" => $row->merchantName,
"mobile" => $row->mobile,
"address" => $row->address,
"city" => $row->city,
"province" => $row->province,
"product" => array(
"productID" => $row->ProductID,
"pMerchantID" => $row->pMerchantID,
"category" => $row->category,
"productName" => $row->productName,
"description" => $row->description,
"price" => $row->price,
"image" => $this->baseUrl . 'imagesProducts/' . $row->image,
"ratingCount" => $row->ratingCount
)
);
array_push($this->resultToEncode, $objItemArray);
}
http_response_code(200);
$httpStatusCode = '200 OK';
$pass = true;
// return json_encode($resultToEncode);
} else {
http_response_code(204);
$httpStatusCode = '204 No Content';
$pass = false;
$this->resultToEncode = 'No Record Found';
}
} catch (PDOException $pdoEx) {
http_response_code(500); // internal server error.
$httpStatusCode = '500 Internal Server Error';
$pass = false;
$this->resultToEncode = $pdoEx->getCode();
} catch (Exception $ex) {
// return $ex->getMessage();
http_response_code(404); // 404 Not Found.
$httpStatusCode = '404 Not Found';
$pass = false;
$this->resultToEncode = $ex->getMessage();
}
} else {
http_response_code(400);
$httpStatusCode = '400 bad request';
$pass = false;
$this->resultToEncode = 'User id not specified';
}
echo json_encode(array('passed' => $pass, 'Response' => $httpStatusCode, 'result' => $this->resultToEncode));
}

Paypal transaction insertin '0' as value

have this paypal page which is working fine only when paypal posts back its not inserting my correct value (By this I mean its inserting '0'), please take a look...
<?php
require 'config.inc.php';
$p = new Paypal();
if(isset($_GET['period']))
{
$allowedPeriods = array("1000", "5000", "10000", "20000");
if(!in_array($_GET['period'], $allowedPeriods))
{
die("Allowed periods are '1000', '5000', '10000', '20000'");
}
if(!$usersClass->checkLoggedIn())
{
die("You must be logged in");
}
$prices = array( "1000" => 10, "5000" => 30, "10000" => 50, "20000" => 85 );
$this_script = "http://".$_SERVER['HTTP_HOST']."/paypal.php?act=";
$p->add_field('business', PAYPAL_EMAIL_ADDRESS);
$p->add_field('return', $this_script.'success');
$p->add_field('cancel_return', $this_script.'cancel');
$p->add_field('notify_url', $this_script.'ipn');
$p->add_field('item_name', 'Credits');
$p->add_field('cmd', '_xclick');
$p->add_field('amount', $prices[$_GET['period']]);
$p->add_field('custom', $usersClass->userID()."||".$_POST['period']);
$p->add_field('rm', '2');
$p->add_field('currency_code','GBP');
$p->submit_paypal_post();
}
if(isset($_GET['act'])) {
switch ($_GET['act']) {
case "cancel": print "Order was canceled!";
break;
case "success":
print "If payment was successfully received you should be on the top!";
break;
case "ipn": if ($p->validate_ipn()) {
$custom = $_POST['custom'];
$explode = explode("||", $custom);
$userid = (int) $explode[0];
$days = $explode[1];
$daysArray = array(10 => 1000, 30 => 5000, 50 => 10000, 85 => 20000 );
$days = $daysArray[$days];
$tillDate = $days;
mysql_query("insert into `featured` values (null, '$userid', '$tillDate')")
}
break;
}
}
?>
I suggest you set error_reporting(E_ALL) and do a post yourself on the ipn script to test it out. 0 could also mean FALSE before using (int) in front of it so it's crucial that you open all errors and see the response yourself.

Categories