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;
+}
+