I am planning to integrate a Google Checkout payment system on a social networking website. The idea is that members can buy "tokens" for real money (which are sort of the website currency) and then they can buy access to some extra content on the website etc.
What I want to do is create a Google Checkout button that takes a member to the checkout page where he pays with his credit or debit card. What I want is the Google Checkout to notify notify my server whether the purchase of tokens was successful (if the credit/debit card was charged) so I can update the local database.
The website is coded in PHP/MySQL.
I have downloaded the sample PHP code from here: code.google.com/p/google-checkout-php-sample-code/wiki/Documentation
I know how to create a Google checkout button and I have also placed the responsehandlerdemo.php file on my server. This is the file the Google Checkout is supposed to send response to (of course I set the path to the file in Google merchant account).
Now in the response handler file there is a switch block with several case statements. Which one means that the payment was successful and I can add tokens to the member account in the local database?
switch ($root) {
case "request-received": {
break;
}
case "error": {
break;
}
case "diagnosis": {
break;
}
case "checkout-redirect": {
break;
}
case "merchant-calculation-callback": {
// Create the results and send it
$merchant_calc = new GoogleMerchantCalculations($currency);
// Loop through the list of address ids from the callback
$addresses = get_arr_result($data[$root]['calculate']['addresses']['anonymous-address']);
foreach($addresses as $curr_address) {
$curr_id = $curr_address['id'];
$country = $curr_address['country-code']['VALUE'];
$city = $curr_address['city']['VALUE'];
$region = $curr_address['region']['VALUE'];
$postal_code = $curr_address['postal-code']['VALUE'];
// Loop through each shipping method if merchant-calculated shipping
// support is to be provided
if(isset($data[$root]['calculate']['shipping'])) {
$shipping = get_arr_result($data[$root]['calculate']['shipping']['method']);
foreach($shipping as $curr_ship) {
$name = $curr_ship['name'];
//Compute the price for this shipping method and address id
$price = 12; // Modify this to get the actual price
$shippable = "true"; // Modify this as required
$merchant_result = new GoogleResult($curr_id);
$merchant_result->SetShippingDetails($name, $price, $shippable);
if($data[$root]['calculate']['tax']['VALUE'] == "true") {
//Compute tax for this address id and shipping type
$amount = 15; // Modify this to the actual tax value
$merchant_result->SetTaxDetails($amount);
}
if(isset($data[$root]['calculate']['merchant-code-strings']
['merchant-code-string'])) {
$codes = get_arr_result($data[$root]['calculate']['merchant-code-strings']
['merchant-code-string']);
foreach($codes as $curr_code) {
//Update this data as required to set whether the coupon is valid, the code and the amount
$coupons = new GoogleCoupons("true", $curr_code['code'], 5, "test2");
$merchant_result->AddCoupons($coupons);
}
}
$merchant_calc->AddResult($merchant_result);
}
} else {
$merchant_result = new GoogleResult($curr_id);
if($data[$root]['calculate']['tax']['VALUE'] == "true") {
//Compute tax for this address id and shipping type
$amount = 15; // Modify this to the actual tax value
$merchant_result->SetTaxDetails($amount);
}
$codes = get_arr_result($data[$root]['calculate']['merchant-code-strings']
['merchant-code-string']);
foreach($codes as $curr_code) {
//Update this data as required to set whether the coupon is valid, the code and the amount
$coupons = new GoogleCoupons("true", $curr_code['code'], 5, "test2");
$merchant_result->AddCoupons($coupons);
}
$merchant_calc->AddResult($merchant_result);
}
}
$Gresponse->ProcessMerchantCalculations($merchant_calc);
break;
}
case "new-order-notification": {
$Gresponse->SendAck();
break;
}
case "order-state-change-notification": {
$Gresponse->SendAck();
$new_financial_state = $data[$root]['new-financial-order-state']['VALUE'];
$new_fulfillment_order = $data[$root]['new-fulfillment-order-state']['VALUE'];
switch($new_financial_state) {
case 'REVIEWING': {
break;
}
case 'CHARGEABLE': {
//$Grequest->SendProcessOrder($data[$root]['google-order-number']['VALUE']);
//$Grequest->SendChargeOrder($data[$root]['google-order-number']['VALUE'],'');
break;
}
case 'CHARGING': {
break;
}
case 'CHARGED': {
break;
}
case 'PAYMENT_DECLINED': {
break;
}
case 'CANCELLED': {
break;
}
case 'CANCELLED_BY_GOOGLE': {
//$Grequest->SendBuyerMessage($data[$root]['google-order-number']['VALUE'],
// "Sorry, your order is cancelled by Google", true);
break;
}
default:
break;
}
switch($new_fulfillment_order) {
case 'NEW': {
break;
}
case 'PROCESSING': {
break;
}
case 'DELIVERED': {
break;
}
case 'WILL_NOT_DELIVER': {
break;
}
default:
break;
}
break;
}
case "charge-amount-notification": {
//$Grequest->SendDeliverOrder($data[$root]['google-order-number']['VALUE'],
// <carrier>, <tracking-number>, <send-email>);
//$Grequest->SendArchiveOrder($data[$root]['google-order-number']['VALUE'] );
$Gresponse->SendAck();
break;
}
case "chargeback-amount-notification": {
$Gresponse->SendAck();
break;
}
case "refund-amount-notification": {
$Gresponse->SendAck();
break;
}
case "risk-information-notification": {
$Gresponse->SendAck();
break;
}
default:
$Gresponse->SendBadRequestStatus("Invalid or not supported Message");
break;
}
I guess that case 'CHARGED' is the one, am I right?
Second question, do I need an SSL certificate to receive response from Google Checkout? According to this I do: groups.google.com/group/google-checkout-api-php/browse_thread/thread/10ce55177281c2b0
But I don's see it mentioned anywhere in the official documentation.
Thank you.
I integrated this into my site over 6 months ago. It's very low volume, but works good so far.
The first thing that you should worry about is 'CHARGEABLE'. This means that the credit card has been approved for the transaction, but it will not actually charge the funds until you take action. In order to send the charge request, simply un-comment the two lines under CHARGEABLE. You can change your settings to make it automatically charge the card in 'settings' > 'preferences', but you might as well just un-comment the 2 lines and leave your options open.
Note that you might want to WAIT for the 'risk-information-notification' and determine if the risk check passed before approving the charge ($data[$root]['risk-information']['eligible-for-protection']['VALUE']). Although, seems you are talking about digital goods the possibility of chargebacks might not matter to you.
At some point, I'm sure you should also check that the request has sufficient information for you to link the funds to some account before you charge it, but maybe this is just my paranoia.
The other state that I use is 'charge-amount-notification'. It's completely possible that there is a way to use 'CHARGED', but I don't that 'CHARGED' provides an amount that was actually charged. ($amount_charged = $data[$root]['total-charge-amount']['VALUE'];)
As for the SSL, if you check the location where you enter the callback URL it states the following:
"Specify a URL for Google to notify you of new orders and changes in order state. You must provide the URL of a server running 128-bit SSLv3 or TLS"
Answer to your comment:
I do this under 'new_order_notification', not sure if you can do it elsewhere.
$items = get_arr_result( $data[$root]['shopping-cart']['items']['item'] );
foreach( $items as $item ) {
if( !isset ( $item['merchant-item-id']['VALUE'] ) ) {
//error
return;
}
$request_item_id = $item['merchant-item-id']['VALUE'];
//save your item id with corresponding google order id for further processing
}
Yes, "Chargeable" is the first thing you need to look at in a Google Checkout order. When you click "Chargeable", a window will pop up for you to actually charge the order BUT be sure that the "Eligible for Protection" is True before actually charging the order. This ensure you that the payment is covered by Google payment guarantee. You can actually see it in the "Buyer Credit Verification" section in Google Checkout.
Related
I have successfully utilized PayPal-PHP-SKD to request a payment sending the user to the PayPal payment gateway sandbox with credentials and sandbox buyer account etc. Everything works and I get a request to the returnURL with $_GET variables success=true, paymentID, token and PayerID. My problem is in the next step which captures the paymentID and PayerID, creates objects for Payment PaymentExecution which seems to work OK but crashes when I execute the line "result = $payment->execute($exClaimPayment, $mPPcredentials);". If I comment out that line, the code works without error but when I include it the code crashes.
"result = $payment->execute($exClaimPayment, $mPPcredentials);"
if (isset($_GET['success'], $_GET['paymentId'], $_GET['PayerID'])) {
if ($_GET['success'] == 'true') {
$mSuccess = TRUE;
$mPaymentID = $_GET['paymentId'];
$mPayerID = $_GET['PayerID'];
$payment = Payment::get($mPaymentID, $mPPcredentials);
$exClaimPayment = New PaymentExecution();
$exClaimPayment->setPayerId($mPayerID);
$mProgress = 'in success after $exClaimPayment->setPayerId($mPayerID)';
try {
$mProgress = 'in try';
//result = $payment->execute($exClaimPayment, $mPPcredentials);
} catch(Exception $ex){
$errorMsg = json_decode($ex->getData());
}
}
} else {
$mSuccess = FALSE;
$mProgress = 'in NOT success';
}
In my envoronment, Win 10, Notepad++, FileZilla, Hostmonnster hosting and Chrome, I cannot see the error. It just crashes (with HTTP 500 ??)
I found my error! It was MY error.
The line:
result = $payment->execute($exClaimPayment, $mPPcredentials);
Should have been:
$result = $payment->execute($exClaimPayment, $mPPcredentials);
I have worked on this code for half a day and did not see my error until 5 minutes after I posted the question on StackOverflow.
My conspicuous errors in PHP often cost me a lot of time. I would benefit from an environment that would point out my syntax errors.
StackOverflow is a very good resource. Thanks!
I use this code to send credential to users in Paid membership pro plugin:
https://gist.github.com/strangerstudios/5331923
In checkout I use PayPal Express and pay by check;
I modified this code because I would send credentials only to PayPal users when getting the checkout. In my code it seems it doesn't recognize the gateway PayPal.
function my_generate_passwords() {
//Get primary gateway
$setting_gateway = get_option("pmpro_gateway");
if (!empty($_REQUEST['username']) && empty($_REQUEST['password']) && $setting_gateway == "paypalexpress") { //if paypalexpress
$_REQUEST['password'] = pmpro_getDiscountCode() . pmpro_getDiscountCode(); //using two random discount codes if paypalexpress
$_REQUEST['password2'] = $_REQUEST['password'];
}
// If pay by check ... I try to insert else to see if the system do the switch condition
else {
$_REQUEST['password'] = pmpro_getDiscountCode(); //using one random discount codes if paybycheck
$_REQUEST['password2'] = $_REQUEST['password'];
}
}
add_action("init", "my_generate_passwords");
Am I wrong?
I create a new payment gateway in cscart. After get success result from payment gateway cs cart redirect it to checkout page and show incomplete checkout.Please help me to solve this issue.
Because you did not provide any code, I made an example code for processing the response:
if ($mode == 'return') {
// this means, that the payment processor returned from 3rd party checkout page
$order_info = fn_get_order_info($_REQUEST['order_id'], true);
// you should have a response code (this section depends on your payment gateway)
if ($_REQUEST['response_code'] == "S") {
// the transaction was successful!
$pp_response['order_status'] = 'P';
$pp_response['transaction_id'] = $_REQUEST['transaction_id'];
$pp_response["reason_text"] = $_REQUEST['response_code'] . ": " . $_REQUEST['transaction_id']);
fn_finish_payment($_REQUEST['order_id'], $pp_response, false);
fn_order_placement_routines('route', $_REQUEST['order_id']);
} else {
// the transaction was NOT successful!
$pp_response['order_status'] = 'N';
$pp_response['transaction_id'] = $_REQUEST['transaction_id'];
$pp_response["reason_text"] = $_REQUEST['response_code'] . ": " . $_REQUEST['transaction_id']);
fn_order_placement_routines('route', $_REQUEST['order_id'], false);
}
}
The key "functions" are: fn_finish_payment() and fn_order_placement_routines(). If you did not finish the payment, you will be redirected to the checkout page, because this means, something went wrong.
I'm asking this question in order to share a solution code.
Context: Apple introduced the AppReceipt in iOS 7. It is also present for OS X IAP. This receipt is a PKCS#7 container (asn.1) with a payload which is also asn.1 structured.
Documentation from Apple instructs how to control the validity of the receipt on-device and to parse it to check that is has been issued for the current device.
There are also instructions to validate the receipt through an application server by contacting Apple server. In that latter case though, the returned json data from Apple does not include information identifying the originating device. Previous IAP protocol model with transactionReceipt included the identifierForVendor UID in the json.
Question: How to parse the binary receipt on a server, using PHP, to check the UID hash, to ensure this receipt belongs to this device? This may be done before or after sending the receipt to Apple server.
This script only check for the hash and not the whole receipt signature validity. This work is left to Apple by sending them the receipt as documented.
The hash check is directly adapted from the Apple documented example code in C. The tricky task here being to find the right pieces of information out of the binary receipt.
This code is using an ASN1 parser by Kris Bailey, link is also in the source code.
You need to change one comment in the parser script code: comment line #189 and uncomment #190. Also the last function in the parser script is unused and can be deleted.
<?php
//$vendID should be a binary string. If you have the vendorID as an ASCII string, convert it back
// $vendID = hex2bin(str_replace('-', '', $vendID_string)); //PHP 5.4+
$vendID = hextobin(str_replace('-', '', $vendID_string)); //PHP 5.3- function below
require_once 'ans1.php'; //donwnload from http://www.phpkode.com/source/s/mistpark-server/library/asn1.php
$asn_parser = new ASN_BASE;
//parse the receipt binary string
$pkcs7 = $asn_parser->parseASNString($receipt->bin);
// $asn_parser->printASN($pkcs7); //uncomment this line to print and inspect PKCS7 container
//target the payload object inside the container
$payload_sequence = $pkcs7[0]->asnData[1]->asnData[0]->asnData[2]->asnData;
//control the OID of payload
if ($payload_sequence[0]->asnData != '1.2.840.113549.1.7.1') {
echo "invalide payload OID";
exit;
}
//the payload octet_string is itself an ASN1 structure. Parse it.
$payload = $asn_parser->parseASNString($payload_sequence[1]->asnData[0]->asnData);
// $asn_parser->printASN($payload); //uncomment this line to print and inspect payload ASN structure
$payload_attributes = $payload[0]->asnData; //array of ASN_SEQUENCE
foreach ($payload_attributes as $attr) {
$type = $attr->asnData[0]->asnData;
switch ($type) {
case 2:
$bundle_id = $attr->asnData[2]->asnData;
break;
// case 3:
// $bundle_version = $attr->asnData[2]->asnData;
// break;
case 4:
$opaque = $attr->asnData[2]->asnData;
break;
case 5:
$hash = $attr->asnData[2]->asnData;
break;
default:
break;
}
}
//compute the hash
$hash_loc = sha1($vendID . $opaque . $bundle_id, true);
//control hash equality
if ($hash_loc == $hash) {
echo "OK\n";
}
else {
echo "KO\n";
}
echo "</pre>\n";
//*******************************************************
function hextobin($hexstr) {
$n = strlen($hexstr);
$sbin = '';
for ($i = 0; $i < $n; $i += 2) $sbin .= pack("H*", substr($hexstr,$i,2));
return $sbin;
}
?>
I want to add yahoo chat icon inside my website. To detect ID status (online/offline), and opening messenger tool, I've wrote the PHP codes below (given from a website). But it always returns ID is offline. Does Yahoo prevents checking status? What's the problem?
<?php
$yahoo_id = 'some_id';
$getstatus = file_get_contents('http://mail.opi.yahoo.com/online?u='.$yahoo_id.'&m=a&t=1');
switch($getstatus) {
case "00": $status = '<img src="images/yahoo_offline.png" />';
break;
case "01": $status = '<img src="images/yahoo_online.png" />';
break;
}
echo $status;
?>