├── .gitignore ├── LICENSE ├── README.md ├── composer.json ├── phpunit.xml ├── src ├── Core │ └── SnapApiCore.php ├── Exception │ ├── AuthenticateException.php │ ├── HttpException.php │ ├── SignatureException.php │ └── SnapBiException.php ├── Interfaces │ ├── ConfigInterface.php │ ├── HttpResponseInterface.php │ └── SnapApiInterface.php ├── Services │ ├── BCA │ │ ├── BcaConfig.php │ │ ├── BcaSnapApi.php │ │ └── Traits │ │ │ ├── HasAccessToken.php │ │ │ ├── HasTransaction.php │ │ │ └── HasVirtualAccount.php │ ├── BRI │ │ ├── BriConfig.php │ │ ├── BriSnapApi.php │ │ └── Traits │ │ │ ├── HasAccessToken.php │ │ │ └── HasTransaction.php │ ├── Config.php │ ├── DANA │ │ ├── DanaConfig.php │ │ ├── DanaSnapApi.php │ │ └── Traits │ │ │ ├── HasAccessToken.php │ │ │ └── HasTransaction.php │ ├── Mandiri │ │ ├── MandiriConfig.php │ │ ├── MandiriSnapApi.php │ │ └── Traits │ │ │ ├── HasAccessToken.php │ │ │ └── HasTransaction.php │ └── SnapBi.php ├── Support │ ├── Http.php │ ├── HttpResponse.php │ └── Signature.php ├── Traits │ ├── HasConfig.php │ └── HasSelfCall.php └── helper.php └── tests ├── Fixtures └── Fixture.example.php └── Unit ├── ConfigTest.php ├── SignatureTest.php └── SupportHttpTest.php /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | 3 | composer.lock 4 | composer.phar 5 | vendor/ 6 | .phpunit.* 7 | .code-coverage 8 | tests/Fixtures/Fixture.php 9 | 10 | index.php -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Otnansirk 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 SNAP BI 2 | This is a PHP library that serves as a wrapper for the SNAP API BI. It is fully compatible with Composer. 3 | 4 | ### Documentation 5 | https://php-snap-bi.gitbook.io/docs 6 | 7 | ## Installation 8 | ```bash 9 | composer require otnansirk/php-snap-bi 10 | ``` 11 | or 12 | ```json 13 | { 14 | "require": { 15 | "otnansirk/php-snap-bi": "1.*" 16 | } 17 | } 18 | ``` 19 | **Next :** 20 | visit [php-snap-bi Documentation](https://php-snap-bi.gitbook.io/docs/getting-started/configuration) for more detail. 21 | 22 | ## Contributing 23 | Thank you for considering contributing to the `php-snap-bi` package. The contribution guide can be found in the `php-snap-bi` [php-snap-bi documentation](https://php-snap-bi.gitbook.io/contribution-guide). 24 | 25 | ## Code of Conduct 26 | In order to ensure that the Laravel community is welcoming to all, please review and abide by the [Code of Conduct](https://php-snap-bi.gitbook.io/docs/contribution-guide/code-of-conduct). 27 | 28 | ## License 29 | The Laravel framework is open-sourced software licensed under the [MIT license](https://opensource.org/license/mit). 30 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "otnansirk/php-snap-bi", 3 | "description": "This is a standard national open API Payments wrapper for PHP", 4 | "keywords": [ 5 | "snap-bi", 6 | "php-snap-bi", 7 | "sdk-snap-bca", 8 | "sdk-snap-mandiri", 9 | "sdk-snap-dana", 10 | "sdk-snap-bri", 11 | "php-snap-bca", 12 | "php-snap-mandiri", 13 | "php-snap-dana", 14 | "php-snap-bri", 15 | "php" 16 | ], 17 | "type": "library", 18 | "license": "MIT", 19 | "authors": [ 20 | { 21 | "name": "Otnansirk", 22 | "email": "iam.otnansirk@gmail.com" 23 | } 24 | ], 25 | "support": { 26 | "forum": "https://github.com/otnansirk/php-snap-bi/discussions", 27 | "issues": "https://github.com/otnansirk/php-snap-bi/issues", 28 | "source": "https://github.com/otnansirk/php-snap-bi", 29 | "docs": "https://php-snap-bi.gitbook.io/docs" 30 | }, 31 | "scripts": { 32 | "test": "XDEBUG_MODE=coverage phpunit" 33 | }, 34 | "require": { 35 | "php": ">=7.4", 36 | "ext-curl": "*", 37 | "ramsey/uuid": "^4.7", 38 | "nesbot/carbon": "^2.71" 39 | }, 40 | "require-dev": { 41 | "psy/psysh": "^0.11.20", 42 | "phpunit/phpunit": "^10.3" 43 | }, 44 | "autoload": { 45 | "psr-4": { 46 | "Otnansirk\\SnapBI\\": "src/" 47 | }, 48 | "files": [ 49 | "src/helper.php" 50 | ] 51 | }, 52 | "autoload-dev": { 53 | "psr-4": { 54 | "Otnansirk\\SnapBI\\": [ 55 | "tests/", 56 | "tests/Integration", 57 | "tests/Unit", 58 | "tests/Fixtures" 59 | ] 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | tests/Unit 9 | 10 | 11 | tests/Integration 12 | 13 | 14 | 15 | 16 | src 17 | 18 | 19 | src/Support/HttpResponse.php 20 | src/Support/Http.php 21 | src/Exception 22 | src/Interfaces 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /src/Core/SnapApiCore.php: -------------------------------------------------------------------------------- 1 | BcaConfig::get('channel_id'), 25 | 'X-PARTNER-ID' => BcaConfig::get('partner_id'), 26 | 'X-EXTERNAL-ID' => int_rand(16), 27 | ]; 28 | } 29 | 30 | /** 31 | * Default body 32 | * 33 | * @return array 34 | */ 35 | public static function defaultBody(): array 36 | { 37 | return [ 38 | "partnerReferenceNo" => Uuid::uuid4(), 39 | "accountNo" => BcaConfig::get('account_id'), 40 | "bankCardToken" => BcaConfig::get('bank_card_token') 41 | ]; 42 | } 43 | } -------------------------------------------------------------------------------- /src/Services/BCA/BcaSnapApi.php: -------------------------------------------------------------------------------- 1 | object()->accessToken; 30 | } 31 | 32 | return new self; 33 | } 34 | 35 | /** 36 | * Throw error if not authenticated 37 | * 38 | * @return void 39 | */ 40 | public static function authenticated() 41 | { 42 | if (!self::$token) { 43 | throw new AuthenticateException("Unauthorized: Please provide an access token", 400); 44 | } 45 | } 46 | 47 | /** 48 | * Get access token 49 | * 50 | * @return HttpResponse 51 | */ 52 | public static function accessTokenB2b(): HttpResponseInterface 53 | { 54 | $path = BcaConfig::get('base_url') . "/openapi/v1.0/access-token/b2b"; 55 | $headers = [ 56 | 'X-TIMESTAMP' => currentTimestamp()->toIso8601String(), 57 | 'X-CLIENT-KEY' => BcaConfig::get('client_id'), 58 | 'X-SIGNATURE' => Signature::asymmetric(BcaConfig::class), 59 | ]; 60 | 61 | $body = ['grantType' => 'client_credentials']; 62 | return Http::withHeaders($headers)->post($path, $body); 63 | } 64 | } -------------------------------------------------------------------------------- /src/Services/BCA/Traits/HasTransaction.php: -------------------------------------------------------------------------------- 1 | toIso8601String(); 28 | $accessToken = self::$token; 29 | 30 | $body = array_merge([ 31 | "partnerReferenceNo" => Uuid::uuid4(), 32 | "accountNo" => BcaConfig::get('account_id'), 33 | "fromDateTime" => $startDate, 34 | "toDateTime" => $endDate, 35 | "bankCardToken" => BcaConfig::get('bank_card_token') 36 | ], self::$body); 37 | 38 | $headers = array_merge([ 39 | 'X-TIMESTAMP' => $timestamp, 40 | 'X-SIGNATURE' => Signature::symmetric(BcaConfig::class, 'POST', $path, $body, $timestamp, $accessToken), 41 | 'CHANNEL-ID' => BcaConfig::get('channel_id'), 42 | 'X-PARTNER-ID' => BcaConfig::get('partner_id'), 43 | 'X-EXTERNAL-ID' => int_rand(16), 44 | ], self::$headers); 45 | 46 | $url = BcaConfig::get('base_url') . $path; 47 | return Http::withToken($accessToken) 48 | ->withHeaders($headers) 49 | ->post($url, $body); 50 | } 51 | 52 | /** 53 | * This service is used to pre-processing OneKlik Registration by Generating OneKlik Registration Web Token. 54 | * 55 | * @return HttpResponseInterface 56 | */ 57 | public static function directDebitPayment(): HttpResponseInterface 58 | { 59 | // Required access token 60 | self::authenticated(); 61 | $path = "/openapi/oneklik/v1.0/debit/payment-host-to-host"; 62 | 63 | $timestamp = currentTimestamp()->toIso8601String(); 64 | $accessToken = self::$token; 65 | 66 | $body = array_merge([ 67 | "partnerReferenceNo" => Uuid::uuid4(), 68 | "accountNo" => BcaConfig::get('account_id'), 69 | "merchantId" => BcaConfig::get('partner_id'), 70 | "bankCardToken" => BcaConfig::get('bank_card_token') 71 | ], self::$body); 72 | 73 | $headers = array_merge([ 74 | 'X-TIMESTAMP' => $timestamp, 75 | 'X-SIGNATURE' => Signature::symmetric(BcaConfig::class, 'POST', $path, $body, $timestamp, $accessToken), 76 | 'CHANNEL-ID' => BcaConfig::get('channel_id'), 77 | 'X-PARTNER-ID' => BcaConfig::get('partner_id'), 78 | 'X-EXTERNAL-ID' => int_rand(16), 79 | ], self::$headers); 80 | 81 | $url = BcaConfig::get('base_url') . $path; 82 | return Http::withToken($accessToken) 83 | ->withHeaders($headers) 84 | ->post($url, $body); 85 | } 86 | 87 | /** 88 | * This service is used to inquiry OneKlik transaction status. 89 | * 90 | * @return HttpResponseInterface 91 | */ 92 | public static function directDebitPaymentStatus(): HttpResponseInterface 93 | { 94 | // Required access token 95 | self::authenticated(); 96 | $path = "/openapi/oneklik/v1.0/debit/status"; 97 | 98 | $timestamp = currentTimestamp()->toIso8601String(); 99 | $accessToken = self::$token; 100 | 101 | $body = array_merge([ 102 | "partnerReferenceNo" => Uuid::uuid4(), 103 | "serviceCode" => '54', 104 | ], self::$body); 105 | 106 | $headers = array_merge([ 107 | 'X-TIMESTAMP' => $timestamp, 108 | 'X-SIGNATURE' => Signature::symmetric(BcaConfig::class, 'POST', $path, $body, $timestamp, $accessToken), 109 | 'CHANNEL-ID' => BcaConfig::get('channel_id'), 110 | 'X-PARTNER-ID' => BcaConfig::get('partner_id'), 111 | 'X-EXTERNAL-ID' => int_rand(16), 112 | ], self::$headers); 113 | 114 | $url = BcaConfig::get('base_url') . $path; 115 | return Http::withToken($accessToken) 116 | ->withHeaders($headers) 117 | ->post($url, $body); 118 | } 119 | 120 | /** 121 | * this is function transfer rtgs 122 | * @return HttpResponseInterface 123 | * @throws \Otnansirk\SnapBI\Exception\AuthenticateException 124 | * @throws \Otnansirk\SnapBI\Exception\HttpException 125 | */ 126 | public static function transferRTGS(): HttpResponseInterface 127 | { 128 | // Required access token 129 | self::authenticated(); 130 | $path = "/openapi/v1.0/transfer-rtgs"; 131 | 132 | $timestamp = currentTimestamp()->toIso8601String(); 133 | $accessToken = self::$token; 134 | 135 | $body = array_merge([ 136 | "partnerReferenceNo" => Uuid::uuid4(), 137 | ], self::$body); 138 | 139 | $headers = array_merge([ 140 | 'X-TIMESTAMP' => $timestamp, 141 | 'X-SIGNATURE' => Signature::symmetric(BcaConfig::class, 'POST', $path, $body, $timestamp, $accessToken), 142 | 'CHANNEL-ID' => BcaConfig::get('channel_id'), 143 | 'X-PARTNER-ID' => BcaConfig::get('partner_id'), 144 | 'X-EXTERNAL-ID' => int_rand(16), 145 | ], self::$headers); 146 | 147 | $url = BcaConfig::get('base_url') . $path; 148 | return Http::withToken($accessToken) 149 | ->withHeaders($headers) 150 | ->post($url, $body); 151 | } 152 | 153 | /** 154 | * This service is used to transfer SKNBI. 155 | * @return HttpResponseInterface 156 | * @throws \Otnansirk\SnapBI\Exception\AuthenticateException 157 | * @throws \Otnansirk\SnapBI\Exception\HttpException 158 | */ 159 | public static function transferSKNBI(): HttpResponseInterface 160 | { 161 | // Required access token 162 | self::authenticated(); 163 | $path = "/openapi/v1.0/transfer-skn"; 164 | 165 | $timestamp = currentTimestamp()->toIso8601String(); 166 | $accessToken = self::$token; 167 | 168 | $body = array_merge([ 169 | "partnerReferenceNo" => Uuid::uuid4(), 170 | "sourceAccountNo" => BcaConfig::get('source_account_no') 171 | ], self::$body); 172 | 173 | $headers = array_merge([ 174 | 'X-TIMESTAMP' => $timestamp, 175 | 'X-SIGNATURE' => Signature::symmetric(BcaConfig::class, 'POST', $path, $body, $timestamp, $accessToken), 176 | 'CHANNEL-ID' => BcaConfig::get('channel_id'), 177 | 'X-PARTNER-ID' => BcaConfig::get('partner_id'), 178 | 'X-EXTERNAL-ID' => int_rand(16), 179 | ], self::$headers); 180 | 181 | $url = BcaConfig::get('base_url') . $path; 182 | return Http::withToken($accessToken) 183 | ->withHeaders($headers) 184 | ->post($url, $body); 185 | } 186 | 187 | /** 188 | * This service is used to transfer intrabank. 189 | * means that the transfer is made within the same bank network 190 | * @return HttpResponseInterface 191 | * @throws \Otnansirk\SnapBI\Exception\AuthenticateException 192 | * @throws \Otnansirk\SnapBI\Exception\HttpException 193 | */ 194 | public static function transferIntraBank(): HttpResponseInterface 195 | { 196 | // Required access token 197 | self::authenticated(); 198 | $path = "/openapi/v1.0/transfer-intrabank"; 199 | 200 | $timestamp = currentTimestamp()->toIso8601String(); 201 | $accessToken = self::$token; 202 | 203 | $body = array_merge([ 204 | "partnerReferenceNo" => Uuid::uuid4(), 205 | "sourceAccountNo" => BcaConfig::get('source_account_no'), 206 | ], self::$body); 207 | 208 | $headers = array_merge([ 209 | 'X-TIMESTAMP' => $timestamp, 210 | 'X-SIGNATURE' => Signature::symmetric(BcaConfig::class, 'POST', $path, $body, $timestamp, $accessToken), 211 | 'CHANNEL-ID' => BcaConfig::get('channel_id'), 212 | 'X-PARTNER-ID' => BcaConfig::get('partner_id'), 213 | 'X-EXTERNAL-ID' => int_rand(16), 214 | ], self::$headers); 215 | 216 | $url = BcaConfig::get('base_url') . $path; 217 | return Http::withToken($accessToken) 218 | ->withHeaders($headers) 219 | ->post($url, $body); 220 | } 221 | 222 | 223 | /** 224 | * This service is used to transfer interbank online. 225 | * meaning that the sender uses a different Bank network to transfer to the receiver 226 | * @return HttpResponseInterface 227 | * @throws \Otnansirk\SnapBI\Exception\AuthenticateException 228 | * @throws \Otnansirk\SnapBI\Exception\HttpException 229 | */ 230 | public static function transferInterBankONL(): HttpResponseInterface 231 | { 232 | // Required access token 233 | self::authenticated(); 234 | $path = "/openapi/v2.0/transfer-interbank"; 235 | 236 | $timestamp = currentTimestamp()->toIso8601String(); 237 | $accessToken = self::$token; 238 | 239 | $body = array_merge([ 240 | "partnerReferenceNo" => Uuid::uuid4(), 241 | "sourceAccountNo" => BcaConfig::get("source_account_no"), 242 | ], self::$body); 243 | 244 | $headers = array_merge([ 245 | 'X-TIMESTAMP' => $timestamp, 246 | 'X-SIGNATURE' => Signature::symmetric(BcaConfig::class, 'POST', $path, $body, $timestamp, $accessToken), 247 | 'CHANNEL-ID' => BcaConfig::get('channel_id'), 248 | 'X-PARTNER-ID' => BcaConfig::get('partner_id'), 249 | 'X-EXTERNAL-ID' => int_rand(16), 250 | ], self::$headers); 251 | 252 | $url = BcaConfig::get('base_url') . $path; 253 | return Http::withToken($accessToken) 254 | ->withHeaders($headers) 255 | ->post($url, $body); 256 | } 257 | 258 | /** 259 | * This service is used to internal account inquiry. 260 | * @return HttpResponseInterface 261 | * @throws \Otnansirk\SnapBI\Exception\AuthenticateException 262 | * @throws \Otnansirk\SnapBI\Exception\HttpException 263 | */ 264 | public static function internalAccountInquiry(): HttpResponseInterface 265 | { 266 | // Required access token 267 | self::authenticated(); 268 | $path = "/openapi/v1.0/account-inquiry-internal"; 269 | 270 | $timestamp = currentTimestamp()->toIso8601String(); 271 | $accessToken = self::$token; 272 | 273 | $body = array_merge([ 274 | "partnerReferenceNo" => Uuid::uuid4(), 275 | ], self::$body); 276 | 277 | $headers = array_merge([ 278 | 'X-TIMESTAMP' => $timestamp, 279 | 'X-SIGNATURE' => Signature::symmetric(BcaConfig::class, 'POST', $path, $body, $timestamp, $accessToken), 280 | 'CHANNEL-ID' => BcaConfig::get('channel_id'), 281 | 'X-PARTNER-ID' => BcaConfig::get('partner_id'), 282 | 'X-EXTERNAL-ID' => int_rand(16), 283 | ], self::$headers); 284 | 285 | $url = BcaConfig::get('base_url') . $path; 286 | return Http::withToken($accessToken) 287 | ->withHeaders($headers) 288 | ->post($url, $body); 289 | } 290 | 291 | /** 292 | * This service is used to internal account inquiry. 293 | * @return HttpResponseInterface 294 | * @throws \Otnansirk\SnapBI\Exception\AuthenticateException 295 | * @throws \Otnansirk\SnapBI\Exception\HttpException 296 | */ 297 | public static function accountInquiryExternal(): HttpResponseInterface 298 | { 299 | // Required access token 300 | self::authenticated(); 301 | $path = "/openapi/v1.0/account-inquiry-external"; 302 | 303 | $timestamp = currentTimestamp()->toIso8601String(); 304 | $accessToken = self::$token; 305 | 306 | $body = array_merge([ 307 | "partnerReferenceNo" => Uuid::uuid4() 308 | ], self::$body); 309 | 310 | $headers = array_merge([ 311 | 'X-TIMESTAMP' => $timestamp, 312 | 'X-SIGNATURE' => Signature::symmetric(BcaConfig::class, 'POST', $path, $body, $timestamp, $accessToken), 313 | 'CHANNEL-ID' => BcaConfig::get('channel_id'), 314 | 'X-PARTNER-ID' => BcaConfig::get('partner_id'), 315 | 'X-EXTERNAL-ID' => int_rand(16), 316 | ], self::$headers); 317 | 318 | $url = BcaConfig::get('base_url') . $path; 319 | return Http::withToken($accessToken) 320 | ->withHeaders($headers) 321 | ->post($url, $body); 322 | } 323 | 324 | /** 325 | * This service is used to inquiry status transaction. 326 | * @return HttpResponseInterface 327 | * @throws \Otnansirk\SnapBI\Exception\AuthenticateException 328 | * @throws \Otnansirk\SnapBI\Exception\HttpException 329 | */ 330 | public static function inquiryStatusTransaction(): HttpResponseInterface 331 | { 332 | // Required access token 333 | self::authenticated(); 334 | $path = "/openapi/v1.0/balance-inquiry"; 335 | 336 | $timestamp = currentTimestamp()->toIso8601String(); 337 | $accessToken = self::$token; 338 | 339 | $body = array_merge([ 340 | "originalPartnerReferenceNo" => Uuid::uuid4() 341 | ], self::$body); 342 | 343 | $headers = array_merge([ 344 | 'X-TIMESTAMP' => $timestamp, 345 | 'X-SIGNATURE' => Signature::symmetric(BcaConfig::class, 'POST', $path, $body, $timestamp, $accessToken), 346 | 'CHANNEL-ID' => BcaConfig::get('channel_id'), 347 | 'X-PARTNER-ID' => BcaConfig::get('partner_id'), 348 | 'X-EXTERNAL-ID' => int_rand(16), 349 | ], self::$headers); 350 | 351 | $url = BcaConfig::get('base_url') . $path; 352 | return Http::withToken($accessToken) 353 | ->withHeaders($headers) 354 | ->post($url, $body); 355 | } 356 | 357 | /** 358 | * This service is used to balance inquiry. 359 | * @return HttpResponseInterface 360 | * @throws \Otnansirk\SnapBI\Exception\AuthenticateException 361 | * @throws \Otnansirk\SnapBI\Exception\HttpException 362 | */ 363 | public static function balanceInquiry(): HttpResponseInterface 364 | { 365 | // Required access token 366 | self::authenticated(); 367 | $path = "/openapi/v1.0/balance-inquiry"; 368 | 369 | $timestamp = currentTimestamp()->toIso8601String(); 370 | $accessToken = self::$token; 371 | 372 | $body = array_merge([ 373 | "partnerReferenceNo" => Uuid::uuid4(), 374 | "accountNo" => BcaConfig::get('account_id'), 375 | "bankCardToken" => BcaConfig::get('bank_card_token'), 376 | ], 377 | self::$body); 378 | 379 | $headers = array_merge([ 380 | 'X-TIMESTAMP' => $timestamp, 381 | 'X-SIGNATURE' => Signature::symmetric(BcaConfig::class, 'POST', $path, $body, $timestamp, $accessToken), 382 | 'CHANNEL-ID' => BcaConfig::get('channel_id'), 383 | 'X-PARTNER-ID' => BcaConfig::get('partner_id'), 384 | 'X-EXTERNAL-ID' => int_rand(16), 385 | ], self::$headers); 386 | 387 | $url = BcaConfig::get('base_url') . $path; 388 | return Http::withToken($accessToken) 389 | ->withHeaders($headers) 390 | ->post($url, $body); 391 | } 392 | 393 | /** 394 | * This service is used to transfer Bi fast. 395 | * @return HttpResponseInterface 396 | * @throws \Otnansirk\SnapBI\Exception\AuthenticateException 397 | * @throws \Otnansirk\SnapBI\Exception\HttpException 398 | */ 399 | public static function transferBIFAST(): HttpResponseInterface 400 | { 401 | // Required access token 402 | self::authenticated(); 403 | $path = "/openapi/v2.0/transfer-interbank"; 404 | 405 | $timestamp = currentTimestamp()->toIso8601String(); 406 | $accessToken = self::$token; 407 | 408 | $body = array_merge([ 409 | "partnerReferenceNo" => Uuid::uuid4(), 410 | "sourceAccountNo" => BcaConfig::get('source_account_no'), 411 | ], 412 | self::$body); 413 | 414 | $headers = array_merge([ 415 | 'X-TIMESTAMP' => $timestamp, 416 | 'X-SIGNATURE' => Signature::symmetric(BcaConfig::class, 'POST', $path, $body, $timestamp, $accessToken), 417 | 'CHANNEL-ID' => BcaConfig::get('channel_id'), 418 | 'X-PARTNER-ID' => BcaConfig::get('partner_id'), 419 | 'X-EXTERNAL-ID' => int_rand(16), 420 | ], self::$headers); 421 | 422 | $url = BcaConfig::get('base_url') . $path; 423 | return Http::withToken($accessToken) 424 | ->withHeaders($headers) 425 | ->post($url, $body); 426 | } 427 | } -------------------------------------------------------------------------------- /src/Services/BCA/Traits/HasVirtualAccount.php: -------------------------------------------------------------------------------- 1 | toIso8601String(); 26 | $accessToken = self::$token; 27 | 28 | $body = array_merge(BcaConfig::defaultBody(), self::$body); 29 | 30 | $headers = array_merge( 31 | BcaConfig::defaultHeaders(), 32 | [ 33 | 'X-TIMESTAMP' => $timestamp, 34 | 'X-SIGNATURE' => Signature::symmetric(BcaConfig::class, 'POST', $path, $body, $timestamp, $accessToken), 35 | ], 36 | self::$headers 37 | ); 38 | 39 | $url = BcaConfig::get('base_url') . $path; 40 | return Http::withToken($accessToken) 41 | ->withHeaders($headers) 42 | ->post($url, $body); 43 | } 44 | 45 | /** 46 | * This service is used to VA Payment Status. 47 | * 48 | * @return HttpResponseInterface 49 | */ 50 | public static function vaInquiryStatus(): HttpResponseInterface 51 | { 52 | // Required access token 53 | self::authenticated(); 54 | $path = "/openapi/v1.0/transfer-va/status"; 55 | 56 | $timestamp = currentTimestamp()->toIso8601String(); 57 | $accessToken = self::$token; 58 | 59 | $body = array_merge(BcaConfig::defaultBody(), self::$body); 60 | 61 | $headers = array_merge( 62 | BcaConfig::defaultHeaders(), 63 | [ 64 | 'X-TIMESTAMP' => $timestamp, 65 | 'X-SIGNATURE' => Signature::symmetric(BcaConfig::class, 'POST', $path, $body, $timestamp, $accessToken), 66 | ], 67 | self::$headers 68 | ); 69 | 70 | $url = BcaConfig::get('base_url') . $path; 71 | return Http::withToken($accessToken) 72 | ->withHeaders($headers) 73 | ->post($url, $body); 74 | } 75 | 76 | /** 77 | * This service is used to VA Payment Flag. 78 | * 79 | * @return HttpResponseInterface 80 | */ 81 | public static function vaPayment(): HttpResponseInterface 82 | { 83 | // Required access token 84 | self::authenticated(); 85 | $path = "/openapi/v1.0/transfer-va/payment"; 86 | 87 | $timestamp = currentTimestamp()->toIso8601String(); 88 | $accessToken = self::$token; 89 | 90 | $body = array_merge(BcaConfig::defaultBody(), self::$body); 91 | 92 | $headers = array_merge( 93 | BcaConfig::defaultHeaders(), 94 | [ 95 | 'X-TIMESTAMP' => $timestamp, 96 | 'X-SIGNATURE' => Signature::symmetric(BcaConfig::class, 'POST', $path, $body, $timestamp, $accessToken), 97 | ], 98 | self::$headers 99 | ); 100 | 101 | $url = BcaConfig::get('base_url') . $path; 102 | return Http::withToken($accessToken) 103 | ->withHeaders($headers) 104 | ->post($url, $body); 105 | } 106 | 107 | /** 108 | * This service is used to VA transfer BillPresentment. 109 | * 110 | * @return HttpResponseInterface 111 | */ 112 | public static function vaInquiryIntrabank(): HttpResponseInterface 113 | { 114 | // Required access token 115 | self::authenticated(); 116 | $path = "/openapi/v1.0/transfer-va/inquiry-intrabank"; 117 | 118 | $timestamp = currentTimestamp()->toIso8601String(); 119 | $accessToken = self::$token; 120 | 121 | $body = array_merge(BcaConfig::defaultBody(), self::$body); 122 | 123 | $headers = array_merge( 124 | BcaConfig::defaultHeaders(), 125 | [ 126 | 'X-TIMESTAMP' => $timestamp, 127 | 'X-SIGNATURE' => Signature::symmetric(BcaConfig::class, 'POST', $path, $body, $timestamp, $accessToken), 128 | ], 129 | self::$headers 130 | ); 131 | 132 | $url = BcaConfig::get('base_url') . $path; 133 | return Http::withToken($accessToken) 134 | ->withHeaders($headers) 135 | ->post($url, $body); 136 | } 137 | 138 | /** 139 | * SNAP Virtual Account Payment to VA from Intrabank 140 | * This service is used to VA transfer. 141 | * 142 | * @return HttpResponseInterface 143 | */ 144 | public static function vaPayIntrabank(): HttpResponseInterface 145 | { 146 | // Required access token 147 | self::authenticated(); 148 | $path = "/openapi/v1.0/transfer-va/payment-intrabank"; 149 | 150 | $timestamp = currentTimestamp()->toIso8601String(); 151 | $accessToken = self::$token; 152 | 153 | $body = array_merge(BcaConfig::defaultBody(), self::$body); 154 | 155 | $headers = array_merge( 156 | BcaConfig::defaultHeaders(), 157 | [ 158 | 'X-TIMESTAMP' => $timestamp, 159 | 'X-SIGNATURE' => Signature::symmetric(BcaConfig::class, 'POST', $path, $body, $timestamp, $accessToken), 160 | ], 161 | self::$headers 162 | ); 163 | 164 | $url = BcaConfig::get('base_url') . $path; 165 | return Http::withToken($accessToken) 166 | ->withHeaders($headers) 167 | ->post($url, $body); 168 | } 169 | 170 | /** 171 | * This service is used to Notification VA transfer. 172 | * 173 | * @return HttpResponseInterface 174 | */ 175 | public static function vaNotifyPayIntrabank(): HttpResponseInterface 176 | { 177 | // Required access token 178 | self::authenticated(); 179 | $path = "/openapi/v1.0/transfer-va/notify-payment-intrabank"; 180 | 181 | $timestamp = currentTimestamp()->toIso8601String(); 182 | $accessToken = self::$token; 183 | 184 | $body = array_merge(BcaConfig::defaultBody(), self::$body); 185 | 186 | $headers = array_merge( 187 | BcaConfig::defaultHeaders(), 188 | [ 189 | 'X-TIMESTAMP' => $timestamp, 190 | 'X-SIGNATURE' => Signature::symmetric(BcaConfig::class, 'POST', $path, $body, $timestamp, $accessToken), 191 | ], 192 | self::$headers 193 | ); 194 | 195 | $url = BcaConfig::get('base_url') . $path; 196 | return Http::withToken($accessToken) 197 | ->withHeaders($headers) 198 | ->post($url, $body); 199 | } 200 | 201 | } -------------------------------------------------------------------------------- /src/Services/BRI/BriConfig.php: -------------------------------------------------------------------------------- 1 | object()->accessToken; 31 | } 32 | 33 | return new self; 34 | } 35 | 36 | /** 37 | * Throw error if not authenticated 38 | * 39 | * @return void 40 | */ 41 | public static function authenticated() 42 | { 43 | if (!self::$token) { 44 | throw new AuthenticateException("Unauthorized: Please provide an access token", 400); 45 | } 46 | } 47 | 48 | /** 49 | * Get access token 50 | * 51 | * @return HttpResponse 52 | */ 53 | public static function accessTokenB2b(): HttpResponseInterface 54 | { 55 | $carbon = Carbon::now('Asia/Jakarta'); 56 | $timestamp = $carbon->format('Y-m-d\TH:i:s.000P'); // outputs: 2021-11-02T13:14:15.000+07:00 57 | $path = BriConfig::get('base_url') . "/snap/v1.0/access-token/b2b"; 58 | $headers = [ 59 | 'X-TIMESTAMP' => $timestamp, 60 | 'X-CLIENT-KEY' => BriConfig::get('client_id'), 61 | 'X-SIGNATURE' => Signature::asymmetricBri(BriConfig::class,$timestamp), 62 | ]; 63 | 64 | $body = ['grantType' => 'client_credentials']; 65 | return Http::withHeaders($headers)->post($path, $body); 66 | } 67 | } -------------------------------------------------------------------------------- /src/Services/BRI/Traits/HasTransaction.php: -------------------------------------------------------------------------------- 1 | toIso8601String(); 28 | $accessToken = self::$token; 29 | 30 | $body = array_merge([ 31 | "partnerReferenceNo" => Uuid::uuid4(), 32 | "accountNo" => BriConfig::get('account_id'), 33 | "fromDateTime" => $startDate, 34 | "toDateTime" => $endDate, 35 | "bankCardToken" => BriConfig::get('bank_card_token') 36 | ], self::$body); 37 | 38 | $headers = array_merge([ 39 | 'X-TIMESTAMP' => $timestamp, 40 | 'X-SIGNATURE' => Signature::symmetric(BriConfig::class, 'POST', $path, $body, $timestamp, $accessToken), 41 | 'CHANNEL-ID' => BriConfig::get('channel_id'), 42 | 'X-PARTNER-ID' => BriConfig::get('partner_id'), 43 | 'X-EXTERNAL-ID' => int_rand(16), 44 | ], self::$headers); 45 | 46 | $url = BriConfig::get('base_url') . $path; 47 | return Http::withToken($accessToken) 48 | ->withHeaders($headers) 49 | ->post($url, $body); 50 | } 51 | } -------------------------------------------------------------------------------- /src/Services/Config.php: -------------------------------------------------------------------------------- 1 | BcaConfig::class, 22 | 'mandiri' => MandiriConfig::class, 23 | 'dana' => DanaConfig::class, 24 | // Register new config 25 | ]; 26 | } 27 | 28 | /** 29 | * Getting config by name 30 | * Currently support for 31 | * bca, mandiri, bri 32 | * 33 | * @param string $name 34 | * @return ConfigInterface 35 | */ 36 | static function for (string $name): ConfigInterface 37 | { 38 | if (isset(self::registeredConfig()[$name])) { 39 | return self::registeredConfig()[$name]::call(); 40 | } 41 | 42 | throw new SnapBiException("SNAP config for $name is not registered"); 43 | } 44 | 45 | /** 46 | * If call an inaccessible static method of a class 47 | * will automatically invoke the __callStatic() 48 | * and return Config by method name 49 | * 50 | * @param string $method 51 | * @param array|null $args 52 | * @return ConfigInterface 53 | */ 54 | static function __callStatic(string $method, array $args = null): ConfigInterface 55 | { 56 | if ($args) { 57 | self::for($method)->register($args[0]); 58 | } 59 | return self::for($method); 60 | } 61 | 62 | } -------------------------------------------------------------------------------- /src/Services/DANA/DanaConfig.php: -------------------------------------------------------------------------------- 1 | object()->accessToken; 31 | } 32 | 33 | return new self; 34 | } 35 | 36 | /** 37 | * Throw error if not authenticated 38 | * 39 | * @return void 40 | */ 41 | public static function authenticated() 42 | { 43 | if (!self::$token) { 44 | throw new AuthenticateException("Unauthorized: Please provide an access token", 400); 45 | } 46 | } 47 | 48 | /** 49 | * Get access token 50 | * 51 | * @return HttpResponse 52 | */ 53 | public static function accessTokenB2b(): HttpResponseInterface 54 | { 55 | $path = DanaConfig::get('base_url') . "/v1.0/access-token/b2b.htm"; 56 | $headers = [ 57 | 'X-TIMESTAMP' => currentTimestamp()->toIso8601String(), 58 | 'X-CLIENT-KEY' => DanaConfig::get('client_id'), 59 | 'X-SIGNATURE' => Signature::asymmetric(DanaConfig::class), 60 | ]; 61 | 62 | $body = ['grantType' => 'client_credentials']; 63 | return Http::withHeaders($headers)->post($path, $body); 64 | } 65 | 66 | /** 67 | * Get oAuth url 68 | * 69 | * @param array $params 70 | * @param array $data 71 | * @param bool $sign 72 | * 73 | * @inheritDoc https://dashboard.dana.id/api-docs/read/125 74 | */ 75 | public static function oAuthUrl(array $params = [], array $data = [], bool $sign = false) 76 | { 77 | $seamlessData = count($data) 78 | ? ['seamlessData' => json_encode($data)] 79 | : []; 80 | 81 | $seamlessSign = $sign 82 | ? ['seamlessSign' => Signature::signSHA256withRSA(DanaConfig::class, $data)] 83 | : []; 84 | 85 | $queryParams = [ 86 | 'timestamp' => currentTimestamp()->toIso8601String(), 87 | 'partnerId' => DanaConfig::get('partner_id'), 88 | 'externalId' => Uuid::uuid4()->toString(), 89 | 'channelId' => DanaConfig::get('channel_id'), 90 | 'state' => int_rand(32), 91 | 'scopes' => 'QUERY_BALANCE,PUBLIC_ID', 92 | 'redirectUrl' => DanaConfig::get('redirect_url'), 93 | ...$params, 94 | ...$seamlessData, 95 | ...$seamlessSign 96 | ]; 97 | return DanaConfig::get('web_url') . "/v1.0/get-auth-code?" . http_build_query($queryParams); 98 | } 99 | } -------------------------------------------------------------------------------- /src/Services/DANA/Traits/HasTransaction.php: -------------------------------------------------------------------------------- 1 | toIso8601String(); 29 | $accessToken = self::$token; 30 | 31 | $body = array_merge([ 32 | "partnerReferenceNo" => Uuid::uuid4(), 33 | "merchantId" => DanaConfig::get('account_id'), 34 | ], self::$body); 35 | 36 | $headers = array_merge([ 37 | 'X-TIMESTAMP' => $timestamp, 38 | 'X-SIGNATURE' => Signature::danaAsymetricTransaction(DanaConfig::class, 'POST', $path, $body, $timestamp), 39 | 'CHANNEL-ID' => DanaConfig::get('channel_id'), 40 | 'X-PARTNER-ID' => DanaConfig::get('partner_id'), 41 | 'X-EXTERNAL-ID' => int_rand(16), 42 | ], self::$headers); 43 | 44 | $url = DanaConfig::get('base_url') . $path; 45 | return Http::withToken($accessToken) 46 | ->withHeaders($headers) 47 | ->post($url, $body); 48 | } 49 | } -------------------------------------------------------------------------------- /src/Services/Mandiri/MandiriConfig.php: -------------------------------------------------------------------------------- 1 | object()->accessToken; 30 | } 31 | 32 | return new self; 33 | } 34 | 35 | /** 36 | * Throw error if not authenticated 37 | * 38 | * @return void 39 | */ 40 | public static function authenticated() 41 | { 42 | if (!self::$token) { 43 | throw new AuthenticateException("Unauthorized: Please provide an access token", 400); 44 | } 45 | } 46 | 47 | /** 48 | * Get access token 49 | * 50 | * @return HttpResponse 51 | */ 52 | public static function accessTokenB2b(): HttpResponseInterface 53 | { 54 | $path = MandiriConfig::get('base_url') . "/openapi/auth/v2.0/access-token/b2b"; 55 | $headers = [ 56 | 'X-TIMESTAMP' => currentTimestamp()->toIso8601String(), 57 | 'X-CLIENT-KEY' => MandiriConfig::get('client_id'), 58 | 'X-SIGNATURE' => Signature::asymmetric(MandiriConfig::class), 59 | ]; 60 | 61 | $body = ['grantType' => 'client_credentials']; 62 | return Http::withHeaders($headers)->post($path, $body); 63 | } 64 | } -------------------------------------------------------------------------------- /src/Services/Mandiri/Traits/HasTransaction.php: -------------------------------------------------------------------------------- 1 | toIso8601String(); 28 | $accessToken = self::$token; 29 | 30 | $body = array_merge([ 31 | "partnerReferenceNo" => Uuid::uuid4(), 32 | "accountNo" => MandiriConfig::get('account_id'), 33 | "fromDateTime" => $startDate, 34 | "toDateTime" => $endDate, 35 | "bankCardToken" => MandiriConfig::get('bank_card_token') 36 | ], self::$body); 37 | 38 | $headers = array_merge([ 39 | 'X-TIMESTAMP' => $timestamp, 40 | 'X-SIGNATURE' => Signature::symmetric(MandiriConfig::class, 'POST', $path, $body, $timestamp, $accessToken), 41 | 'CHANNEL-ID' => MandiriConfig::get('channel_id'), 42 | 'X-PARTNER-ID' => MandiriConfig::get('partner_id'), 43 | 'X-EXTERNAL-ID' => int_rand(16), 44 | ], self::$headers); 45 | 46 | $url = MandiriConfig::get('base_url') . $path; 47 | return Http::withToken($accessToken) 48 | ->withHeaders($headers) 49 | ->post($url, $body); 50 | } 51 | } -------------------------------------------------------------------------------- /src/Services/SnapBi.php: -------------------------------------------------------------------------------- 1 | BcaSnapApi::class, 22 | 'mandiri' => MandiriSnapApi::class, 23 | 'dana' => DanaSnapApi::class, 24 | // Register new SnapApi 25 | ]; 26 | } 27 | 28 | /** 29 | * Getting config by name 30 | * Currently support for 31 | * bca, mandiri, bri 32 | * 33 | * @param string $name 34 | * @return SnapApiInterface 35 | */ 36 | static function for (string $name): SnapApiInterface 37 | { 38 | if (isset(self::registeredSnapApi()[$name])) { 39 | return self::registeredSnapApi()[$name]::call(); 40 | } 41 | 42 | throw new SnapBiException("SNAP BI for $name is not registered"); 43 | } 44 | /** 45 | * If call an inaccessible static method of a class 46 | * will automatically invoke the __callStatic() 47 | * and return SnapApi by method name 48 | * 49 | * @param string $method 50 | * @param array|null $args 51 | * @return SnapApiInterface 52 | */ 53 | static function __callStatic(string $method, array $args = null): SnapApiInterface 54 | { 55 | return self::for($method); 56 | } 57 | 58 | } -------------------------------------------------------------------------------- /src/Support/Http.php: -------------------------------------------------------------------------------- 1 | $url, 105 | CURLOPT_HTTPHEADER => $curlHeaders, 106 | CURLOPT_RETURNTRANSFER => true, 107 | CURLOPT_HEADER => true, 108 | CURLOPT_CUSTOMREQUEST => strtoupper($method), 109 | CURLOPT_POSTFIELDS => json_encode($body) 110 | ); 111 | curl_setopt_array($ch, $curlOptions); 112 | 113 | $response = curl_exec($ch); 114 | curl_close($ch); 115 | 116 | // Get http status code info 117 | $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); 118 | 119 | return new HttpResponse($response, $httpCode); 120 | 121 | } catch (\Throwable $th) { 122 | throw new HttpException("Error when request API"); 123 | } 124 | } 125 | 126 | /** 127 | * Format array header to cUrl header 128 | * 129 | * @param array $headers 130 | * @return array 131 | */ 132 | public static function headerFormated(array $headers): array 133 | { 134 | 135 | $result = array(); 136 | foreach ($headers as $key => $value) { 137 | $result[] = $key . ':' . $value; 138 | } 139 | return $result; 140 | } 141 | } -------------------------------------------------------------------------------- /src/Support/HttpResponse.php: -------------------------------------------------------------------------------- 1 | body = $body; 20 | $this->status = $statusCode; 21 | $this->headers = $headers; 22 | } 23 | 24 | /** 25 | * Get body as is 26 | * 27 | * @return string 28 | */ 29 | function body(): string 30 | { 31 | return $this->body; 32 | } 33 | 34 | /** 35 | * Get status code 36 | * 37 | * @return integer 38 | */ 39 | function status(): int 40 | { 41 | return $this->status; 42 | } 43 | 44 | /** 45 | * Get body as object 46 | * 47 | * @return object 48 | */ 49 | function object(): object 50 | { 51 | return json_decode($this->body); 52 | } 53 | 54 | /** 55 | * Get body as array 56 | * 57 | * @return array 58 | */ 59 | function array(): array 60 | { 61 | return json_decode($this->body, true); 62 | } 63 | 64 | /** 65 | * Get headers response 66 | * 67 | * @return array 68 | */ 69 | function headers(): array 70 | { 71 | $headers = explode("\r\n", $this->headers); 72 | 73 | $headerArr = array(); 74 | foreach ($headers as $headerLine) { 75 | list($key, $value) = explode(': ', $headerLine, 2); 76 | $headerArr[$key] = $value; 77 | } 78 | return $headerArr; 79 | } 80 | 81 | /** 82 | * Check is status code not in range 200 - 299 83 | * 84 | * @return boolean 85 | */ 86 | function failed(): bool 87 | { 88 | return !(($this->status >= 200) && ($this->status < 300)); 89 | } 90 | } -------------------------------------------------------------------------------- /src/Support/Signature.php: -------------------------------------------------------------------------------- 1 | toIso8601String(); 19 | 20 | $signature = ""; 21 | if (!openssl_sign($stringToSign, $signature, $privateKey, OPENSSL_ALGO_SHA256)) { 22 | throw new SignatureException("Failed to generate signature"); 23 | } 24 | 25 | // X-SIGNATURE 26 | return base64_encode($signature); 27 | } 28 | 29 | final public static function asymmetricBri(string $config,$timestamp) : String { 30 | $privateKey = $config::get('ssh_private_key'); 31 | $stringToSign = $config::get('client_id').'|'.$timestamp; 32 | $signature = ""; 33 | if (!openssl_sign($stringToSign, $signature, $privateKey, OPENSSL_ALGO_SHA256)) { 34 | throw new SignatureException("Failed to generate signature"); 35 | } 36 | 37 | // X-SIGNATURE 38 | return base64_encode($signature); 39 | } 40 | 41 | /** 42 | * Generate signature symmetric 43 | * 44 | * @param string $config 45 | * @param string $method 46 | * @param string $path 47 | * @param array $body 48 | * @param string $timestamp 49 | * @param string $accessToken 50 | * @return string 51 | */ 52 | final public static function symmetric( 53 | string $config, 54 | string $method, 55 | string $path, 56 | array $body, 57 | string $timestamp, 58 | string $accessToken 59 | ): string { 60 | // Body minify 61 | $hashBody = json_encode($body); 62 | 63 | // Calculate Hash with sha256 64 | $hashBody = hash('sha256', $hashBody); 65 | 66 | // Convert to lowercase 67 | $signedBody = strtolower($hashBody); 68 | 69 | $stringToSign = implode(':', [ 70 | $method, 71 | $path, 72 | $accessToken, 73 | $signedBody, 74 | $timestamp 75 | ]); 76 | 77 | $signature = hash_hmac('sha512', $stringToSign, $config::get('client_secret'), true); 78 | 79 | // X-SIGNATURE 80 | return base64_encode($signature); 81 | } 82 | 83 | /** 84 | * Generate dana signature for transaction 85 | * 86 | * @param string $config 87 | * @param string $method 88 | * @param string $path 89 | * @param array $body 90 | * @param string $timestamp 91 | * @return string 92 | */ 93 | final public static function danaAsymetricTransaction( 94 | string $config, 95 | string $method, 96 | string $path, 97 | array $body, 98 | string $timestamp 99 | ): string { 100 | $privateKey = $config::get('ssh_private_key'); 101 | 102 | // Body minify 103 | $hashBody = json_encode($body); 104 | 105 | // Calculate Hash with sha256 106 | $hashBody = hash('sha256', $hashBody); 107 | 108 | // Convert to lowercase 109 | $signedBody = strtolower($hashBody); 110 | 111 | $stringToSign = implode(':', [ 112 | $method, 113 | $path, 114 | $signedBody, 115 | $timestamp 116 | ]); 117 | 118 | $signature = ""; 119 | if (!openssl_sign($stringToSign, $signature, $privateKey, OPENSSL_ALGO_SHA256)) { 120 | throw new SignatureException("Failed to generate signature"); 121 | } 122 | 123 | // X-SIGNATURE 124 | return base64_encode($signature); 125 | } 126 | 127 | /** 128 | * Sign the seamlessData with generated privateKey and algorithm SHA256withRSA 129 | * 130 | * @param string $config 131 | * @param array $body 132 | * @return string 133 | */ 134 | final public static function signSHA256withRSA(string $config, array $body): string 135 | { 136 | 137 | $privateKey = $config::get('ssh_private_key'); 138 | $signature = ""; 139 | if (!openssl_sign(json_encode($body), $signature, $privateKey, OPENSSL_ALGO_SHA256)) { 140 | throw new SignatureException("Failed to generate signature"); 141 | } 142 | return base64_encode($signature); 143 | } 144 | } -------------------------------------------------------------------------------- /src/Traits/HasConfig.php: -------------------------------------------------------------------------------- 1 | timezone($timezone); 18 | } 19 | } 20 | 21 | if (!function_exists('int_rand')) { 22 | /** 23 | * Random integer only 24 | * 25 | * @param int $length 26 | * @return string 27 | */ 28 | function int_rand(int $length): string 29 | { 30 | $result = ''; 31 | for ($i = 0; $i < $length; $i++) { 32 | $result .= mt_rand(0, 9); 33 | } 34 | return $result; 35 | } 36 | } -------------------------------------------------------------------------------- /tests/Fixtures/Fixture.example.php: -------------------------------------------------------------------------------- 1 | "a82ed8bf-493a-4133-ba01-08129e3w8432", 20 | "client_secret" => "a82ed8bf-493a-4133-ba01-08129e3w8432", 21 | "ssh_private_key" => << << "UAYCORQ011", 63 | "account_id" => "0643002227", 64 | "bank_card_token" => "1234567890", 65 | "channel_id" => "92221", 66 | "base_url" => "https://api.api.com", 67 | ]; 68 | } 69 | } -------------------------------------------------------------------------------- /tests/Unit/ConfigTest.php: -------------------------------------------------------------------------------- 1 | expectException(\Otnansirk\SnapBI\Exception\SnapBiException::class); 11 | Config::bank(Fixture::configFixture()); 12 | } 13 | function testThrowInvalidArgumentException() 14 | { 15 | $this->expectException(\InvalidArgumentException::class); 16 | Config::bca([]); 17 | } 18 | 19 | function testRegister() 20 | { 21 | Config::bca(Fixture::configFixture()); 22 | $this->assertArrayHasKey("client_id", Config::bca()->all()); 23 | $this->assertArrayHasKey("client_id", Config::for('bca')->all()); 24 | } 25 | 26 | function testGet() 27 | { 28 | $this->assertArrayHasKey("client_id", Config::bca()->all()); 29 | $this->assertArrayHasKey("client_id", Config::for('bca')->all()); 30 | } 31 | function testAll() 32 | { 33 | $this->assertNotEmpty(Config::bca()->all()); 34 | $this->assertNotEmpty(Config::for('bca')->all()); 35 | } 36 | } -------------------------------------------------------------------------------- /tests/Unit/SignatureTest.php: -------------------------------------------------------------------------------- 1 | assertIsString($res); 15 | $this->assertNotEmpty($res); 16 | } 17 | 18 | function testSymmetric() 19 | { 20 | Config::bca(Fixture::configFixture()); 21 | $res = Signature::symmetric( 22 | Config::bca()::class, 23 | 'POST', 24 | '/api/path', 25 | ['name' => 'otnansirk'], 26 | currentTimestamp(), 27 | 'accessToken' 28 | ); 29 | 30 | $this->assertIsString($res); 31 | $this->assertNotEmpty($res); 32 | } 33 | } -------------------------------------------------------------------------------- /tests/Unit/SupportHttpTest.php: -------------------------------------------------------------------------------- 1 | ["Authorization " => "Bearer Shdjdxsw"], 11 | "response" => "Authorization :Bearer Shdjdxsw" 12 | ]; 13 | 14 | $res = Http::headerFormated($cases['request']); 15 | 16 | $this->assertEquals($cases['response'], $res[0]); 17 | $this->assertIsArray($res); 18 | } 19 | 20 | function testWithToken() 21 | { 22 | $res = Http::withToken('qwerty'); 23 | $this->assertInstanceOf(Http::class, $res); 24 | } 25 | } --------------------------------------------------------------------------------