PHP skeleton for Booking Server API v3
diff --git a/.htaccess b/.htaccess new file mode 100644 index 0000000..6b6f978 --- /dev/null +++ b/.htaccess
@@ -0,0 +1,34 @@ +# Copyright 2019, Google Inc. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +<IfModule mod_rewrite.c> +Options +FollowSymLinks +RewriteEngine On +RewriteRule ^v3/(.*) bookingserver.php?route=$1 [NC] +</IfModule>
diff --git a/README.md b/README.md new file mode 100644 index 0000000..c5bc859 --- /dev/null +++ b/README.md
@@ -0,0 +1,53 @@ +# Booking Server Skeleton for PHP + +This is a reference implementation of +[API v3 Booking Server](https://developers.google.com/maps-booking/guides/end-to-end-integration/implement-booking-server) +based on PHP + +### Prerequisites + +Requires an installation of + +* [Apache](https://httpd.apache.org/) +* [PHP](https://secure.php.net/) + +## Getting Started + +The Booking Server is implemented using PHP and Apache. To properly route the +requests it makes use of the +[.htaccess file](https://httpd.apache.org/docs/current/howto/htaccess.html) so +make sure that +[AllowOverride](https://httpd.apache.org/docs/current/mod/core.html#allowoverride) +is enabled for the directory. + +It is required that you implement an SSL certificate and have all requests +served over https. If your server does not already have an SSL certificate setup +you can review the +[Apache SSL/TLS documentation](https://httpd.apache.org/docs/2.4/ssl/ssl_howto.html). + +The implementation is also not using protocol buffer libraries, but instead +relies on simple JSON serialization methods. + +To download the project execute the following command: + + git clone https://maps-booking.googlesource.com/php-maps-booking-rest-server-v3-skeleton + +The entire code base consists of only three files: + +* .htaccess instructs apache to route all of the /v3/ requests through + bookingserver.php +* bookingserver.php handles the request logic, including authentication +* apiv3methods.php has the methods implementing API v3 interface + +After you downloaded the files you should place them in a servable directory. +Note that whichever directory you put them in will automatically be the parent +to the /v3/ folder (which you do not need to create). + +## Testing your Booking Server + +For instructions on how to test your booking server refer to the +[booking server testing](https://developers.google.com/maps-booking/guides/end-to-end-integration/test-booking-server) +section of our documentation. It is reccomended that you download and run the +[booking test utility](https://maps-booking.googlesource.com/maps-booking-v3/). +To install it, follow the provided installation instructions in its README page. +
diff --git a/apiv3methods.php b/apiv3methods.php new file mode 100644 index 0000000..8ad6db2 --- /dev/null +++ b/apiv3methods.php
@@ -0,0 +1,209 @@ +<?php +/* + * Copyright 2019, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * HealthCheck method + * https://developers.google.com/maps-booking/reference/rest-api-v3/healthcheck-method + * @param {string} requestBody - HTTP request body + * @return {string} HTTP response body + */ +function healthCheck() { + // TO-DO: add any additional server checks, e.g. database status + // Return a response similar to gRPC Health Check + // https://github.com/grpc/grpc/blob/master/doc/health-checking.md + $res['status'] = 'serving'; + return json_encode($res); +} +/** + * CheckAvailability method + * https://developers.google.com/maps-booking/reference/rest-api-v3/checkavailability-method + * @param {string} requestBody - HTTP request body + * @return {string} HTTP response body + */ + +function checkAvailability($request) { + // CheckAvailabilityRequest + $req = json_decode($request, true); + // TO-DO: validate req, e.g. + // (req.slot !== null && req.slot.merchant_id !== null) + // TO-DO: add code to verify the provided slot availability + // CheckAvailabilityResponse + $resp = [ + 'slot' => $req['slot'], + 'count_available' => 1, + 'duration_requirement' => 'DURATION_REQUIREMENT_UNSPECIFIED' + // TO-DO: populate proper values and other fields, such as + // availability_update + ]; + return json_encode($resp); +} +/** + * CreateBooking method + * https://developers.google.com/maps-booking/reference/rest-api-v3/createbooking-method + * @param {string} requestBody - HTTP request body + * @return {string} HTTP response body + */ + +function createBooking($request) { + // CreateBookingRequest + $req = json_decode($request, true); + + // TO-DO: validate req, e.g. (req.user_information !== null) + // TO-DO: add code to create a booking + // CreateBookingResponse + $resp['booking'] = [ + 'booking_id' => '1234', + 'slot' => $req['slot'], + 'user_information' => ['user_id' => $req['user_information']['user_id']], + 'payment_information' => $req['payment_information'], + 'status' => 'CONFIRMED' + ]; + return json_encode($resp); +} +/** + * UpdateBooking method + * https://developers.google.com/maps-booking/reference/rest-api-v3/updatebooking-method + * @param {string} requestBody - HTTP request body + * @return {string} HTTP response body + */ + +function updateBooking($request) { + // UpdateBookingRequest + $req = json_decode($request, true); + // TO-DO: validate req, e.g. + // (req.booking !== null && req.booking.booking_id !== null) + // TO-DO: add code to update the provided booking + // UpdateBookingResponse + $resp['booking'] = [ + 'booking_id' => $req['booking']['booking_id'], + 'status' => $req['booking']['status'] + ]; + return json_encode($resp); +} +/** + * GetBookingStatus method + * https://developers.google.com/maps-booking/reference/rest-api-v3/getbookingstatus-method + * @param {string} requestBody - HTTP request body + * @return {string} HTTP response body + */ + +function getBookingStatus($request) { + // GetBookingStatusRequest + $req = json_decode($request, true); + // TO-DO: validate req, e.g. (req.booking_id !== null) + // TO-DO: add code to retrieve the booking status + // GetBookingStatusResponse + $resp = [ + 'booking_id' => $req['booking_id'], + 'booking_status' => 'BOOKING_STATUS_UNSPECIFIED' + ]; + return json_encode($resp); +} +/** + * ListBookings method + * https://developers.google.com/maps-booking/reference/rest-api-v3/listbookings-method + * @param {string} requestBody - HTTP request body + * @return {string} HTTP response body + */ + +function listBookings($request) { + // ListBookingsRequest + $req = json_decode($request, true); + // TO-DO: validate req, e.g. (req.user_id !== null) + // TO-DO: add code to fetch all bookings for the user_id + // ListBookingsResponse + $resp = [ + 'booking_id' => $req['booking_id'], + 'booking_status' => 'BOOKING_STATUS_UNSPECIFIED' + ]; + return json_encode($resp); +} +/** + * CheckOrderFulfillability method (Order-based Booking Server only) + * https://developers.google.com/maps-booking/reference/rest-api-v3/checkorderfulfillability-method + * @param {string} requestBody - HTTP request body + * @return {string} HTTP response body + */ +function checkOrderFulfillability($request) { + // CheckOrderFulfillabilityRequest + // TO-DO: validate req, e.g. (req.merchant_id !== null) + // TO-DO: add code to validate individual items and calculate the total price + $req = json_decode($request, true); + // CheckOrderFulfillabilityResponse + $resp = [ + 'fulfillability' => [ + 'result' => 'CAN_FULFILL', + 'item_fulfillability' => [] + ], + 'fees_and_taxes' => ['price_micros' => 1000000, 'currency_code' => 'USD'] + ]; + + return json_encode($resp); +} +/** + * CreateOrder method (Order-based Booking Server only) + * https://developers.google.com/maps-booking/reference/rest-api-v3/createorder-method + * @param {string} requestBody - HTTP request body + * @return {string} HTTP response body + */ +function createOrder($request) { + // CreateOrderRequest + $req = json_decode($request, true); + // TO-DO: validate req, e.g. (req.user_information !== null) + // TO-DO: check for req.idempotency_token uniqueness + // TO-DO: create and process the order + // CreateOrderResponse + $resp['order'] = [ + 'order_id' => '1234', + 'merchant_id' => $req['order']['merchant_id'], + 'item' => [] + ]; + return json_encode($resp); +} +/** + * ListOrders method (Order-based Booking Server only) + * https://developers.google.com/maps-booking/reference/rest-api-v3/listorders-method + * @param {string} requestBody - HTTP request body + * @return {string} HTTP response body + */ +function listOrders($request) { + // ListOrdersRequest + $req = json_decode($request, true); + // TO-DO: validate req, e.g. if ("user_id" in req || "order_ids" in req) + // TO-DO: fetch orders for req.user_id or a list of req.order_ids + // ListOrdersResponse + $resp = [ + 'order' => [] + ]; + return json_encode($resp); +} +
diff --git a/bookingserver.php b/bookingserver.php new file mode 100644 index 0000000..901fb83 --- /dev/null +++ b/bookingserver.php
@@ -0,0 +1,188 @@ +<?php +/* + * Copyright 2019, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +$route = $_GET['route']; +$method = $_SERVER['REQUEST_METHOD']; + +//Ensure all requests are made over https +if(empty($_SERVER['HTTPS']) || $_SERVER['HTTPS'] !== "on") { + header('HTTP/1.0 403 Forbidden'); + echo 'All requests must be made over https'; + die(); +} + +//Implements basic auth for all routes +//Be sure to change the username and password +if($_SERVER['PHP_AUTH_USER'] != 'username' + || $_SERVER['PHP_AUTH_PW'] != 'password') { + header('WWW-Authenticate: Basic'); + header('HTTP/1.0 401 Unauthorized'); + die('Unauthorized'); +} + +require 'apiv3methods.php'; + +//loads the posted data of the request +$request = file_get_contents('php://input'); + +if($method === 'GET') { + //GET /v3/HealthCheck + if($route === 'HealthCheck') { + try { + header('Content-type:application/json'); + echo healthCheck(); + } + catch(Exception $e) { + //TO-DO: add specific error handling + echo $e->getMessage(); + } + } + else { + //an unkown GET request + header('HTTP/1.0 404 Not Found'); + echo '404<br>'; + echo 'Method: ' . $method; + } +} +else if($method === 'POST') { + switch($route) { + // POST /v3/CheckAvailability + case 'CheckAvailability': + try { + header('Content-type:application/json'); + echo checkAvailability($request); + } + catch(Exception $e) { + //TO-DO: add specific error handling + echo $e->getMessage(); + die(); + } + break; + //POST /v3/CreateBooking + case 'CreateBooking': + try { + header('Content-type:application/json'); + echo createBooking($request); + } + catch(Exception $e) { + //TO-DO: add specific error handling + echo $e->getMessage(); + die(); + } + break; + // POST /v3/UpdateBooking + case 'UpdateBooking': + try { + header('Content-type:application/json'); + echo updateBooking($request); + } + catch(Exception $e) { + //TO-DO: add specific error handling + echo $e->getMessage(); + die(); + } + break; + //POST /v3/GetBookingStatus + case 'GetBookingStatus': + try { + header('Content-type:application/json'); + echo getBookingStatus($request); + } + catch(Exception $e) { + //TO-DO: add specific error handling + echo $e->getMessage(); + die(); + } + break; + //POST /v3/ListBookings + case 'ListBookings': + try { + header('Content-type:application/json'); + echo listBookings($request); + } + catch(Exception $e) { + //TO-DO: add specific error handling + echo $e->getMessage(); + die(); + } + break; + //POST /v3/CheckOrderFulfillability + case 'CheckOrderFulfillability': + try { + header('Content-type:application/json'); + echo checkOrderFulfillability($request); + } + catch(Exception $e) { + //TO-DO: add specific error handling + echo $e->getMessage(); + die(); + } + break; + //POST /v3/CreateOrder + case 'CreateOrder': + try { + header('Content-type:application/json'); + echo createOrder($request); + } + catch(Exception $e) { + //TO-DO: add specific error handling + echo $e->getMessage(); + die(); + } + break; + //POST /v3/ListOrders + case 'ListOrders': + try { + header('Content-type:application/json'); + echo listOrders($request); + } + catch(Exception $e) { + //TO-DO: add specific error handling + echo $e->getMessage(); + die(); + } + break; + //An unkown post request + default: + header('HTTP/1.0 404 Not Found'); + echo '404<br>'; + echo 'Method: ' . $method; + } +} +//A request with a non post or get method +else { + header('HTTP/1.0 404 Not Found'); + echo '404<br>'; + echo 'Method: ' . $method; +} +