├── LICENSE ├── README.md ├── config.php ├── index.php ├── oauth.php └── postoauth.php /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Xenith Tech 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # php-shopify-app-skeleton 2 | A bare-bones Shopify app written in plain PHP with no framework 3 | 4 |

This is a very basic stripped down Shopify app that was designed to be as plug and play as possible. It does require a database setup with two tables in it and uses MySQL to work with the database. The table structures are as follows:

5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 |
clients
Column NameTypeNULLKeyDefaultExtra
client_idint(11)NOPRINULLauto_increment
client_namevarchar(255)NONULL
33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 |
client_stores
Column NameTypeNULLKeyDefaultExtra
store_idint(11)NOPRINULLauto_increment
client_idint(11)NONULL
store_namevarchar(255)NONULL
tokenvarchar(255)NONULL
hmacvarchar(255)YESNULL
noncevarchar(255)YESNULL
urlvarchar(255)NONULL
last_activitydatetimeNOCURRENT_TIMESTAMP
activetinyint(4)NO1
117 | 118 | The main purpose of writing this was the lack of something quick, simple, and ready to go. Most tutorials are in Ruby using the `shopify_app` gem. I tried this and had nothing but headaches getting it to work. After having to download multiple pieces of software, plugins, frameworks, etc I had an app that didn't run but still somehow managed to take up around 96MB. 119 | 120 | So I decided to go back to the basics. This app will handle the Oauth handshake sent from Shopify as well as the added security of handling the app page view itself. This was also something I found missing from the tutorials. They handle the handshake to verify the call is coming from Shopify and then redirects to the page/location of where your app is going to run. The issue here is that at this point a hacker can simply go directly to your app location and skip around the handshake. This is asking for trouble. 121 | 122 | This app will also be setup to be scaled in a way the would allow you to make it an external app (not embedded) and be able to have one client handle/work on multiple stores so that it can more easily be used by an agency. It also includes a column in `client_stores` named `active` that can be used to deactivate a store on the back end. 123 | 124 | 125 | ## Setup 126 | 127 | ### Clone the repo and clean up the directory 128 | ``` 129 | git clone https://github.com/XenithTech/php-shopify-app-skeleton.git my_app 130 | cd my_app 131 | rm -rf .git 132 | rm README.md 133 | rm LICENSE 134 | ``` 135 | 136 | ### Create the app in Shopify 137 | 1. In your partners account (go ahead and create one if you don't have one), under `Apps` click on `Create app` in the top right. 138 | 2. Choose public (this is always a better option, in my opinion, because it has greater security measures and if you decide to make the app for another store, you only need the one instance) 139 | 3. Name your app 140 | 4. Set the `App URL` to point to `oauth.php`. This will be where your app is hosted. (ie: `https://your-app-location.com/oauth.php`) 141 | 5. Set the `Allowed redirection URL(s)` to include `postoauth.php` and `index.php`. It should look something like this: 142 | ``` 143 | https://your-app-location.com/postoauth.php 144 | https://your-app-location.com/index.php 145 | ``` 146 | 6. Click `Create app` in the top right 147 | 148 | ### Add your app Key and Secret Key to `config.php` 149 | The next screen after clicking `Create app` should display these keys for you. Inside `config.php` set `$k` to the API key, and then set `$s` to the API secret key 150 | 151 | ### Set your app permissions in `config.php` 152 | Modify the `$permissions` array to contain all permissions your app will need 153 | 154 | ### Connect your database in `config.php` 155 | Create a database containing two tables with the given structure above. Be sure to create/add a user to this database. The permissions this user needs to have are at minimum `SELECT`, `UPDATE` and `INSERT`. 156 | 157 | Inside `config.php` do the following: 158 | 159 | 1. Set `$sn` to your server name. If your database is on the same server this app is hosted it will likely need to be set to `localhost`. Other wise if it is hosted elsewhere it should be set to the IP address of the server hosting the database. 160 | 2. Set `$un` to the database's user account name 161 | 3. Set `$pw` to the user account's password 162 | 4. Set `$dn` to the name of the database 163 | 164 | ### Upload your app to your server 165 | Of course, the final step here is to upload all of the files for the app to your server. Once that is done your app should be ready to be installed on a development store. This is found under `More actions` when viewing your app inside of Shopify Partners. 166 | 167 | ## Build out your app your way 168 | `index.php` is the home of the actual app. If you are wanting to serve the functionality of your app through some means other than PHP (ie: Node.js, React, etc) You simply need to change the `$redirection_url` to the location of your app as well as set this location as whitelisted under `Allowed redirection URL(s)` inside your app settings in your Shopify Partners account. Keep in mind that how ever you host it there is some added security in the `index.php` file that will need to be handled appropriately. 169 | -------------------------------------------------------------------------------- /config.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /index.php: -------------------------------------------------------------------------------- 1 | PDO::ERRMODE_EXCEPTION, 24 | PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC 25 | ); 26 | 27 | $return_check = $pdo = new PDO($dsn, $un, $pw, $opt); 28 | 29 | if($return_check === false){ 30 | echo "Unable to process request. ERROR: I-GCI-1"; 31 | die(); 32 | } 33 | $return_check = $stm = $pdo->prepare("SELECT client_id FROM clients WHERE client_name = ?"); 34 | 35 | if($return_check === false){ 36 | echo "Unable to process request. ERROR: I-GCI-2"; 37 | die(); 38 | } 39 | $return_check = $stm->execute(array($shop)); 40 | 41 | if($return_check === false){ 42 | echo "Unable to process request. ERROR: I-GCI-3"; 43 | die(); 44 | } 45 | $result = $stm->fetchAll(); 46 | 47 | if(count($result) !== 0){ 48 | $client_id = $result[0]['client_id']; 49 | } 50 | else{ 51 | echo "Unable to process request. ERROR: I-GCI-4"; 52 | die(); 53 | } 54 | 55 | return $client_id; 56 | } 57 | 58 | function verifyHMAC($hmac, $client_id){ 59 | 60 | global $sn, $dn, $un, $pw, $s; 61 | 62 | $dsn = "mysql:host=".$sn.";dbname=".$dn.";charset=utf8"; 63 | $opt = array( 64 | PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, 65 | PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC 66 | ); 67 | 68 | $return_check = $pdo = new PDO($dsn, $un, $pw, $opt); 69 | if($return_check === false){ 70 | die("Unable to process request. ERROR: I-VH-1"); 71 | } 72 | $return_check = $stm = $pdo->prepare("SELECT nonce FROM client_stores WHERE client_id = ? AND last_activity >= NOW() - INTERVAL 10 SECOND AND active = 1"); 73 | if($return_check === false){ 74 | die("Unable to process request. ERROR: I-VH-2"); 75 | } 76 | $return_check = $stm->execute(array($client_id)); 77 | 78 | if($return_check === false){ 79 | die("Unable to process request. ERROR: I-VH-3"); 80 | } 81 | 82 | $result = $stm->fetchAll(); 83 | 84 | if(count($result) !== 0){ 85 | $nonce = $result[0]['nonce']; 86 | } 87 | else{ 88 | die("Unable to process request. ERROR: I-VH-4"); 89 | } 90 | 91 | $check = hash_hmac('sha256', $nonce, $s); 92 | 93 | if($check == $hmac){ 94 | return true; 95 | } 96 | else{ 97 | return false; 98 | } 99 | } 100 | ?> 101 | 102 | 103 | 104 | 105 | 106 |
107 |

Welcome to My App, ! (ID: )

108 |
109 | 110 | -------------------------------------------------------------------------------- /oauth.php: -------------------------------------------------------------------------------- 1 | PDO::ERRMODE_EXCEPTION, 55 | PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC 56 | ); 57 | 58 | 59 | 60 | $return_check = $pdo = new PDO($dsn, $un, $pw, $opt); 61 | if($return_check === false){ 62 | echo "Unable to process request. ERROR: O-PC-1"; 63 | die(); 64 | } 65 | $return_check = $stm = $pdo->prepare("SELECT client_id FROM clients WHERE client_name = ?"); 66 | if($return_check === false){ 67 | echo "Unable to process request. ERROR: O-PC-2"; 68 | die(); 69 | } 70 | $return_check = $stm->execute(array($shop)); 71 | 72 | if($return_check === false){ 73 | echo "Unable to process request. ERROR: O-PC-3"; 74 | die(); 75 | } 76 | 77 | $result = $stm->fetchAll(); 78 | 79 | if(count($result) !== 0){ 80 | $client_id = $result[0]['client_id']; 81 | } 82 | else{ 83 | $client_id = createClient($shop); 84 | } 85 | 86 | if($client_id == -1){ 87 | echo "Unable to process request. ERROR: O-PC-4"; 88 | die(); 89 | } 90 | 91 | $return_check = $stm = $pdo->prepare("SELECT store_id FROM client_stores WHERE client_id = ?"); 92 | if($return_check === false){ 93 | echo "Unable to process request. ERROR: O-PC-5"; 94 | die(); 95 | } 96 | $return_check = $stm->execute(array($client_id)); 97 | 98 | if($return_check === false){ 99 | echo "Unable to process request. ERROR: O-PC-6"; 100 | die(); 101 | 102 | } 103 | 104 | $result = $stm->fetchAll(); 105 | 106 | if(count($result) == 0){ 107 | $return_check = $stm = $pdo->prepare("INSERT INTO client_stores (client_id, store_name, url) VALUES (?, ?, ?)"); 108 | 109 | if($return_check === false){ 110 | echo "Unable to process request. ERROR: O-PC-7"; 111 | die(); 112 | } 113 | $return_check = $stm->execute(array($client_id, $shop, "https://".$shop.".myshopify.com/")); 114 | if($return_check === false){ 115 | echo "Unable to process request. ERROR: O-PC-8"; 116 | die(); 117 | } 118 | } 119 | 120 | return $client_id; 121 | } 122 | 123 | function createClient($shop){ 124 | global $sn, $dn, $un, $pw; 125 | 126 | $dsn = "mysql:host=".$sn.";dbname=".$dn.";charset=utf8"; 127 | $opt = array( 128 | PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, 129 | PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC 130 | ); 131 | 132 | $return_check = $pdo = new PDO($dsn, $un, $pw, $opt); 133 | if($return_check === false){ 134 | echo "Unable to process request. ERROR: O-CC-1"; 135 | die(); 136 | } 137 | 138 | $return_check = $stm = $pdo->prepare("INSERT INTO clients (client_name) VALUES (?)"); 139 | 140 | if($return_check === false){ 141 | echo "Unable to process request. ERROR: O-CC-2"; 142 | die(); 143 | } 144 | $return_check = $stm->execute(array($shop)); 145 | if($return_check === false){ 146 | echo "Unable to process request. ERROR: O-CC-3"; 147 | die(); 148 | } 149 | 150 | $return_check = $stm = $pdo->prepare("SELECT client_id FROM clients WHERE client_name = ?"); 151 | if($return_check === false){ 152 | echo "Unable to process request. ERROR: O-CC-4"; 153 | die(); 154 | } 155 | $return_check = $stm->execute(array($shop)); 156 | 157 | if($return_check === false){ 158 | echo "Unable to process request. ERROR: O-CC-5"; 159 | die(); 160 | 161 | } 162 | 163 | $result = $stm->fetchAll(); 164 | 165 | if(count($result) !== 0){ 166 | return $result[0]['client_id']; 167 | } 168 | 169 | return -1; 170 | } 171 | 172 | function generateNonce($client_id){ 173 | $nonce = hash('sha256', makeRandomString()); 174 | storeNonce($client_id, $nonce); 175 | return $nonce; 176 | } 177 | 178 | function makeRandomString($bits = 256) { 179 | $bytes = ceil($bits / 8); 180 | $return = ''; 181 | for ($i = 0; $i < $bytes; $i++) { 182 | $return .= chr(mt_rand(0, 255)); 183 | } 184 | return $return; 185 | } 186 | 187 | function storeNonce($client_id, $nonce){ 188 | global $sn, $dn, $un, $pw; 189 | 190 | $dsn = "mysql:host=".$sn.";dbname=".$dn.";charset=utf8"; 191 | $opt = array( 192 | PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, 193 | PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC 194 | ); 195 | 196 | $return_check = $pdo = new PDO($dsn, $un, $pw, $opt); 197 | if($return_check === false){ 198 | echo "Unable to process request. ERROR: O-SN-1"; 199 | die(); 200 | } 201 | $return_check = $stm = $pdo->prepare("UPDATE client_stores SET nonce = ? WHERE client_id = ?"); 202 | if($return_check === false){ 203 | echo "Unable to process request. ERROR: O-SN-2"; 204 | die(); 205 | } 206 | $return_check = $stm->execute(array($nonce, $client_id)); 207 | 208 | if($return_check === false){ 209 | echo "Unable to process request. ERROR: O-SN-3"; 210 | die(); 211 | 212 | } 213 | } 214 | 215 | ?> -------------------------------------------------------------------------------- /postoauth.php: -------------------------------------------------------------------------------- 1 | $k, 31 | "client_secret" => $s, 32 | "code" => $code 33 | ); 34 | 35 | $access_token_url = "https://" . $_GET['shop'] . "/admin/oauth/access_token"; 36 | 37 | $ch = curl_init(); 38 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); 39 | curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 0); 40 | curl_setopt($ch, CURLOPT_URL, $access_token_url); 41 | curl_setopt($ch, CURLOPT_POST, count($query)); 42 | curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($query)); 43 | $result = curl_exec($ch); 44 | curl_close($ch); 45 | $result = json_decode($result, true); 46 | $access_token = $result['access_token']; 47 | 48 | storeToken($client_id, $access_token); 49 | 50 | $hmac = generateHMAC($client_id); 51 | 52 | header("Location: ".$redirect_url."?shop=".$_GET['shop']."&hmac=".$hmac); 53 | } 54 | else{ 55 | die("Unable to process request. ERROR: PO-R-2"); 56 | } 57 | } 58 | else{ 59 | die("Unable to process request. ERROR: PO-R-3"); 60 | } 61 | 62 | } 63 | 64 | function verifyHMAC(){ 65 | global $s; 66 | global $message; 67 | global $hmac; 68 | 69 | $check = hash_hmac('sha256', $message, $s); 70 | 71 | if($check == $hmac){ 72 | return true; 73 | } 74 | else{ 75 | return false; 76 | } 77 | 78 | } 79 | 80 | function verifyNonce(){ 81 | 82 | global $sn, $dn, $un, $pw, $client_id, $nonce; 83 | 84 | $dsn = "mysql:host=".$sn.";dbname=".$dn.";charset=utf8"; 85 | $opt = array( 86 | PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, 87 | PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC 88 | ); 89 | 90 | $return_check = $pdo = new PDO($dsn, $un, $pw, $opt); 91 | if($return_check === false){ 92 | die("Unable to process request. ERROR: PO-VN-1"); 93 | } 94 | $return_check = $stm = $pdo->prepare("SELECT nonce FROM client_stores WHERE client_id = ?"); 95 | if($return_check === false){ 96 | die("Unable to process request. ERROR: PO-VN-2"); 97 | } 98 | $return_check = $stm->execute(array($client_id)); 99 | 100 | if($return_check === false){ 101 | die("Unable to process request. ERROR: PO-VN-3"); 102 | } 103 | 104 | $result = $stm->fetchAll(); 105 | 106 | if(count($result) !== 0){ 107 | $check = $result[0]['nonce']; 108 | } 109 | else{ 110 | die("Unable to process request. ERROR: PO-VN-4"); 111 | } 112 | 113 | $return_check = $stm = $pdo->prepare("UPDATE client_stores SET nonce = '' WHERE client_id = ?"); 114 | if($return_check === false){ 115 | echo "Unable to process request. ERROR: PO-VN-5"; 116 | die(); 117 | } 118 | $return_check = $stm->execute(array($client_id)); 119 | 120 | if($return_check === false){ 121 | echo "Unable to process request. ERROR: PO-VN-6"; 122 | die(); 123 | } 124 | 125 | if($nonce == $check){ 126 | return true; 127 | } 128 | 129 | return false; 130 | } 131 | 132 | function verifyHost(){ 133 | global $shop; 134 | if(endswith($shop, '.myshopify.com')){ 135 | $shop = str_replace('.myshopify.com', '', $shop); 136 | if(preg_match('/[a-z\.\-0-9]/i', $shop)){ 137 | return true; 138 | } 139 | 140 | return false; 141 | } 142 | } 143 | 144 | function processClient($shop){ 145 | 146 | global $sn, $dn, $un, $pw; 147 | 148 | $client_id = -1; 149 | 150 | $dsn = "mysql:host=".$sn.";dbname=".$dn.";charset=utf8"; 151 | $opt = array( 152 | PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, 153 | PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC 154 | ); 155 | 156 | $return_check = $pdo = new PDO($dsn, $un, $pw, $opt); 157 | if($return_check === false){ 158 | die("Unable to process request. ERROR: PO-PC-1"); 159 | } 160 | $return_check = $stm = $pdo->prepare("SELECT client_id FROM clients WHERE client_name = ?"); 161 | if($return_check === false){ 162 | die("Unable to process request. ERROR: PO-PC-2"); 163 | } 164 | $return_check = $stm->execute(array(str_replace('.myshopify.com', '', $shop))); 165 | 166 | if($return_check === false){ 167 | die("Unable to process request. ERROR: PO-PC-3"); 168 | } 169 | 170 | $result = $stm->fetchAll(); 171 | 172 | if(count($result) !== 0){ 173 | return $result[0]['client_id']; 174 | } 175 | else{ 176 | die("Unable to process request. ERROR: PO-PC-4"); 177 | } 178 | } 179 | 180 | function generateHMAC($client_id){ 181 | global $s; 182 | 183 | $nonce = generateNonce($client_id); 184 | 185 | $hmac = hash_hmac('sha256', $nonce, $s); 186 | 187 | storeHMAC($client_id, $hmac); 188 | 189 | return $hmac; 190 | } 191 | 192 | 193 | function storeHMAC($client_id, $hmac){ 194 | global $sn, $dn, $un, $pw; 195 | 196 | $dsn = "mysql:host=".$sn.";dbname=".$dn.";charset=utf8"; 197 | $opt = array( 198 | PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, 199 | PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC 200 | ); 201 | 202 | ini_set('display_errors', 'Off'); 203 | 204 | $return_check = $pdo = new PDO($dsn, $un, $pw, $opt); 205 | if($return_check === false){ 206 | echo "Unable to process request. ERROR: PO-SH-1"; 207 | die(); 208 | } 209 | $return_check = $stm = $pdo->prepare("UPDATE client_stores SET hmac = ?, last_activity = NOW() WHERE client_id = ?"); 210 | if($return_check === false){ 211 | echo "Unable to process request. ERROR: PO-SH-2"; 212 | die(); 213 | } 214 | $return_check = $stm->execute(array($hmac, $client_id)); 215 | 216 | if($return_check === false){ 217 | echo "Unable to process request. ERROR: PO-SH-3"; 218 | die(); 219 | } 220 | } 221 | 222 | function generateNonce($client_id){ 223 | $nonce = hash('sha256', makeRandomString()); 224 | storeNonce($client_id, $nonce); 225 | return $nonce; 226 | } 227 | 228 | function makeRandomString($bits = 256) { 229 | $bytes = ceil($bits / 8); 230 | $return = ''; 231 | for ($i = 0; $i < $bytes; $i++) { 232 | $return .= chr(mt_rand(0, 255)); 233 | } 234 | return $return; 235 | } 236 | 237 | function storeNonce($client_id, $nonce){ 238 | global $sn, $dn, $un, $pw; 239 | 240 | $dsn = "mysql:host=".$sn.";dbname=".$dn.";charset=utf8"; 241 | $opt = array( 242 | PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, 243 | PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC 244 | ); 245 | 246 | ini_set('display_errors', 'Off'); 247 | 248 | $return_check = $pdo = new PDO($dsn, $un, $pw, $opt); 249 | if($return_check === false){ 250 | echo "Unable to process request. ERROR: PO-SN-1"; 251 | die(); 252 | } 253 | $return_check = $stm = $pdo->prepare("UPDATE client_stores SET nonce = ? WHERE client_id = ?"); 254 | if($return_check === false){ 255 | echo "Unable to process request. ERROR: PO-SN-2"; 256 | die(); 257 | } 258 | $return_check = $stm->execute(array($nonce, $client_id)); 259 | 260 | if($return_check === false){ 261 | echo "Unable to process request. ERROR: PO-SN-3"; 262 | die(); 263 | } 264 | } 265 | 266 | function storeToken($client_id, $token){ 267 | global $sn, $dn, $un, $pw; 268 | 269 | $dsn = "mysql:host=".$sn.";dbname=".$dn.";charset=utf8"; 270 | $opt = array( 271 | PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, 272 | PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC 273 | ); 274 | 275 | ini_set('display_errors', 'Off'); 276 | 277 | $return_check = $pdo = new PDO($dsn, $un, $pw, $opt); 278 | if($return_check === false){ 279 | die("Unable to process request. ERROR: PO-ST-1"); 280 | } 281 | $return_check = $stm = $pdo->prepare("UPDATE client_stores SET token = ? WHERE client_id = ?"); 282 | if($return_check === false){ 283 | die("Unable to process request. ERROR: PO-ST-2"); 284 | } 285 | $return_check = $stm->execute(array($token, $client_id)); 286 | 287 | if($return_check === false){ 288 | die("Unable to process request. ERROR: PO-ST-3"); 289 | 290 | } 291 | } 292 | 293 | function endswith($string, $test) { 294 | $strlen = strlen($string); 295 | $testlen = strlen($test); 296 | if ($testlen > $strlen) return false;{ 297 | return substr_compare($string, $test, $strlen - $testlen, $testlen) === 0; 298 | } 299 | } 300 | 301 | ?> --------------------------------------------------------------------------------