Add the server-side script files

This commit is contained in:
Namhyeon Go 2025-01-01 21:12:42 +09:00
parent 58e9bdc4ab
commit fe4d0d562e
4 changed files with 581 additions and 0 deletions

View File

@ -0,0 +1,61 @@
<?php
// https://github.com/dimamedia/PHP-Simple-TOTP-and-PubKey
class tfa {
// RFC4648 Base32 alphabet
private $alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
function getOtp($key) {
/* Base32 decoder */
// Remove spaces from the given public key and converting to an array
$key = str_split(str_replace(" ","",$key));
$n = 0;
$j = 0;
$binary_key = "";
// Decode public key's each character to base32 and save into binary chunks
foreach($key as $char) {
$n = $n << 5;
$n = $n + stripos($this->alphabet, $char);
$j += 5;
if($j >= 8) {
$j -= 8;
$binary_key .= chr(($n & (0xFF << $j)) >> $j);
}
}
/* End of Base32 decoder */
// current unix time 30sec period as binary
$binary_timestamp = pack('N*', 0) . pack('N*', floor(microtime(true)/30));
// generate keyed hash
$hash = hash_hmac('sha1', $binary_timestamp, $binary_key, true);
// generate otp from hash
$offset = ord($hash[19]) & 0xf;
$otp = (
((ord($hash[$offset+0]) & 0x7f) << 24 ) |
((ord($hash[$offset+1]) & 0xff) << 16 ) |
((ord($hash[$offset+2]) & 0xff) << 8 ) |
(ord($hash[$offset+3]) & 0xff)
) % pow(10, 6);
return $otp;
}
function getPubKey() {
$alphabet = str_split($this->alphabet);
$key = '';
// generate 16 chars public key from Base32 alphabet
for ($i = 0; $i < 16; $i++) $key .= $alphabet[mt_rand(0,31)];
// split into 4x4 chunks for easy reading
return implode(" ", str_split($key, 4));
}
}
?>

View File

@ -0,0 +1,70 @@
<?php
// coupang.class.php
// Coupang Product Search API integration class
// Namhyeon Go <gnh1201@gmail.com>
// https://github.com/gnh1201/welsonjs
//
date_default_timezone_set("GMT+0");
class CoupangProductSearch {
private $accessKey = "";
private $secretKey = "";
private $baseUrl = "https://api-gateway.coupang.com";
private function generateSignature($method, $path, $query = "") {
$datetime = (new \DateTime("now", new \DateTimeZone("GMT")))->format("ymd\THis\Z");
$message = $datetime . $method . $path . $query;
$signature = hash_hmac('sha256', $message, $this->secretKey);
return [
'authorization' => "CEA algorithm=HmacSHA256, access-key={$this->accessKey}, signed-date={$datetime}, signature={$signature}",
'datetime' => $datetime
];
}
public function searchProducts($keyword, $limit = 10, $subId = null, $imageSize = null, $srpLinkOnly = false) {
$path = "/v2/providers/affiliate_open_api/apis/openapi/products/search";
$queryParams = http_build_query([
'keyword' => $keyword,
'limit' => $limit,
'subId' => $subId,
'imageSize' => $imageSize,
'srpLinkOnly' => $srpLinkOnly
]);
$fullPath = $path . '?' . $queryParams;
$url = $this->baseUrl . $fullPath;
$signatureData = $this->generateSignature("GET", $path, $queryParams);
$authorization = $signatureData['authorization'];
$datetime = $signatureData['datetime'];
$headers = [
"Content-Type: application/json;charset=UTF-8",
"Authorization: $authorization"
];
$curl = curl_init();
curl_setopt($curl, CURLOPT_URL, $url);
curl_setopt($curl, CURLOPT_CUSTOMREQUEST, "GET");
curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($curl);
$httpCode = curl_getinfo($curl, CURLINFO_HTTP_CODE);
curl_close($curl);
if ($httpCode === 200) {
return json_decode($response, true);
} else {
try {
return json_decode($response, true);
} catch (Exception $e) {
return [
"status" => $httpCode,
"message" => $e->getMessage()
];
}
}
}
}

View File

@ -0,0 +1,418 @@
<?php
/**
* The MIT License (MIT)
*
* Copyright (c) 2013 mk-j, zedwood.com
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
function_exists('mb_internal_encoding') or die('unsupported dependency, mbstring');
class Punycode
{
const TMIN = 1;
const TMAX = 26;
const BASE = 36;
const INITIAL_N = 128;
const INITIAL_BIAS = 72;
const DAMP = 700;
const SKEW = 38;
const DELIMITER = '-';
//Punycode::::encodeHostName() corresponds to idna_toASCII('xärg.örg');
public static function encodeHostName($hostname)
{
if (!self::is_valid_utf8($hostname))
{
return $hostname;//invalid
}
if (function_exists('idn_to_ascii') && 0)
{
return idn_to_ascii($hostname);//php 5.3+
}
$old_encoding = mb_internal_encoding();
mb_internal_encoding("UTF-8");
$pieces = explode(".", self::mb_strtolower($hostname) );
$punycode_pieces = array();
foreach($pieces as $piece)
{
if (preg_match("/[\x{80}-\x{FFFF}]/u", $piece))//is multi byte utf8
{
$punycode_pieces[] = "xn--".self::encode($piece);
}
else if (preg_match('/^[a-z\d][a-z\d-]{0,62}$/i', $piece) && !preg_match('/-$/', $piece) )//is valid ascii hostname
{
$punycode_pieces[] = $piece;
}
else
{
mb_internal_encoding($old_encoding);
return $hostname;//invalid domain
}
}
mb_internal_encoding($old_encoding);
return implode(".", $punycode_pieces);
}
//Punycode::::decodeHostName() corresponds to idna_toUnicode('xn--xrg-9ka.xn--rg-eka');
public static function decodeHostName($encoded_hostname)
{
if (!preg_match('/[a-z\d.-]{1,255}/', $encoded_hostname))
{
return false;
}
if (function_exists('idn_to_utf8') && 0)
{
return idn_to_utf8($encoded_hostname);
}
$old_encoding = mb_internal_encoding();
mb_internal_encoding("UTF-8");
$pieces = explode(".", strtolower($encoded_hostname));
foreach($pieces as $piece)
{
if (!preg_match('/^[a-z\d][a-z\d-]{0,62}$/i', $piece) || preg_match('/-$/', $piece) )
{
mb_internal_encoding($old_encoding);
return $encoded_hostname;//invalid
}
$punycode_pieces[] = strpos($piece, "xn--")===0 ? self::decode(substr($piece,4)) : $piece;
}
mb_internal_encoding($old_encoding);
return implode(".", $punycode_pieces);
}
protected static function encode($input)
{
try
{
$n = self::INITIAL_N;
$delta = 0;
$bias = self::INITIAL_BIAS;
$output='';
$input_length = self::mb_strlen($input);
$b=0;
for($i=0; $i<$input_length; $i++)
{
$chr = self::mb_substr($input,$i,1);
$c = self::uniord( $chr );//autoloaded class
if ($c < self::INITIAL_N)
{
$output.= $chr;
$b++;
}
}
if ($b==$input_length)//no international chars to convert to punycode here
{
throw new Exception("PunycodeException.BAD_INPUT");
}
else if ($b>0)
{
$output.= self::DELIMITER;
}
$h = $b;
while($h < $input_length)
{
$m = PHP_INT_MAX;
// Find the minimum code point >= n
for($i=0; $i<$input_length; $i++)
{
$chr = self::mb_substr($input,$i,1);
$c = self::uniord( $chr );
if ($c >= $n && $c < $m)
{
$m = $c;
}
}
if (($m - $n) > (PHP_INT_MAX - $delta) / ($h+1))
{
throw new Exception("PunycodeException.OVERFLOW");
}
$delta = $delta + ($m - $n) * ($h + 1);
$n = $m;
for($j=0; $j<$input_length; $j++)
{
$chr = self::mb_substr($input,$j,1);
$c = self::uniord( $chr );
if ($c < $n)
{
$delta++;
if (0==$delta)
{
throw new Exception("PunycodeException.OVERFLOW");
}
}
if ($c == $n)
{
$q = $delta;
for($k= self::BASE;; $k+=self::BASE)
{
$t=0;
if ($k <= $bias)
{
$t= self::TMIN;
} else if ($k >= $bias + self::TMAX) {
$t= self::TMAX;
} else {
$t = $k - $bias;
}
if ($q < $t)
{
break;
}
$output.= chr( self::digit2codepoint($t + ($q - $t) % (self::BASE - $t)) );
$q = floor( ($q-$t) / (self::BASE - $t) );//integer division
}
$output.= chr( self::digit2codepoint($q) );
$bias = self::adapt($delta, $h+1, $h==$b);
$delta=0;
$h++;
}
}
$delta++;
$n++;
}
}
catch (Exception $e)
{
error_log("[PUNYCODE] error ".$e->getMessage());
return $input;
}
return $output;
}
protected static function decode($input)
{
try
{
$n = self::INITIAL_N;
$i = 0;
$bias = self::INITIAL_BIAS;
$output = '';
$d = self::rstrpos($input, self::DELIMITER);
if ($d>0) {
for($j=0; $j<$d; $j++) {
$chr = self::mb_substr($input,$j,1);
$c = self::uniord( $chr );
if ($c>=self::INITIAL_N) {
throw new Exception("PunycodeException.BAD_INPUT");
}
$output.=$chr;
}
$d++;
} else {
$d = 0;
}
$input_length = self::mb_strlen($input);
while ($d < $input_length) {
$oldi = $i;
$w = 1;
for($k= self::BASE;; $k += self::BASE) {
if ($d == $input_length) {
throw new Exception("PunycodeException.BAD_INPUT");
}
$chr = self::mb_substr($input,$d++,1);
$c = self::uniord( $chr );
$digit = self::codepoint2digit($c);
if ($digit > (PHP_INT_MAX - $i) / $w) {
throw new Exception("PunycodeException.OVERFLOW");
}
$i = $i + $digit * $w;
$t=0;
if ($k <= $bias) {
$t = self::TMIN;
} else if ($k >= $bias + self::TMAX) {
$t = self::TMAX;
} else {
$t = $k - $bias;
}
if ($digit < $t) {
break;
}
$w = $w * (self::BASE - $t);
}
$output_length = self::mb_strlen($output);
$bias = self::adapt($i - $oldi, $output_length + 1, $oldi == 0);
if ($i / ($output_length + 1) > PHP_INT_MAX - $n) {
throw new Exception("PunycodeException.OVERFLOW");
}
$n = floor($n + $i / ($output_length + 1));
$i = $i % ($output_length + 1);
$output = self::mb_strinsert($output, self::utf8($n), $i);
$i++;
}
}
catch(Exception $e)
{
error_log("[PUNYCODE] error ".$e->getMessage());
return $input;
}
return $output;
}
//adapt patched from:
//https://github.com/takezoh/php-PunycodeEncoder/blob/master/punycode.php
protected static function adapt($delta, $numpoints, $firsttime)
{
$delta = (int)($firsttime ? $delta / self::DAMP : $delta / 2);
$delta += (int)($delta / $numpoints);
$k = 0;
while ($delta > (((self::BASE - self::TMIN) * self::TMAX) / 2)) {
$delta = (int)($delta / (self::BASE - self::TMIN));
$k += self::BASE;
}
return $k + (int)((self::BASE - self::TMIN + 1) * $delta / ($delta + self::SKEW));
}
protected static function digit2codepoint($d)
{
if ($d < 26) {
// 0..25 : 'a'..'z'
return $d + ord('a');
} else if ($d < 36) {
// 26..35 : '0'..'9';
return $d - 26 + ord('0');
} else {
throw new Exception("PunycodeException.BAD_INPUT");
}
}
protected static function codepoint2digit($c)
{
if ($c - ord('0') < 10) {
// '0'..'9' : 26..35
return $c - ord('0') + 26;
} else if ($c - ord('a') < 26) {
// 'a'..'z' : 0..25
return $c - ord('a');
} else {
throw new Exception("PunycodeException.BAD_INPUT");
}
}
protected static function rstrpos($haystack, $needle)
{
$pos = strpos (strrev($haystack), $needle);
if ($pos === false)
return false;
return strlen ($haystack)-1 - $pos;
}
protected static function mb_strinsert($haystack, $needle, $position)
{
$old_encoding = mb_internal_encoding();
mb_internal_encoding("UTF-8");
$r = mb_substr($haystack,0,$position).$needle.mb_substr($haystack,$position);
mb_internal_encoding($old_encoding);
return $r;
}
protected static function mb_substr($str,$start,$length)
{
$old_encoding = mb_internal_encoding();
mb_internal_encoding("UTF-8");
$r = mb_substr($str,$start,$length);
mb_internal_encoding($old_encoding);
return $r;
}
protected static function mb_strlen($str)
{
$old_encoding = mb_internal_encoding();
mb_internal_encoding("UTF-8");
$r = mb_strlen($str);
mb_internal_encoding($old_encoding);
return $r;
}
protected static function mb_strtolower($str)
{
$old_encoding = mb_internal_encoding();
mb_internal_encoding("UTF-8");
$r = mb_strtolower($str);
mb_internal_encoding($old_encoding);
return $r;
}
public static function uniord($c)//cousin of ord() but for unicode
{
$ord0 = ord($c[0]); if ($ord0>=0 && $ord0<=127) return $ord0;
$ord1 = ord($c[1]); if ($ord0>=192 && $ord0<=223) return ($ord0-192)*64 + ($ord1-128);
if ($ord0==0xed && ($ord1 & 0xa0) == 0xa0) return false; //code points, 0xd800 to 0xdfff
$ord2 = ord($c[2]); if ($ord0>=224 && $ord0<=239) return ($ord0-224)*4096 + ($ord1-128)*64 + ($ord2-128);
$ord3 = ord($c[3]); if ($ord0>=240 && $ord0<=247) return ($ord0-240)*262144 + ($ord1-128)*4096 + ($ord2-128)*64 + ($ord3-128);
return false;
}
public static function utf8($num)//cousin of ascii() but for utf8
{
if($num<=0x7F) return chr($num);
if($num<=0x7FF) return chr(($num>>6)+192).chr(($num&63)+128);
if(0xd800<=$num && $num<=0xdfff) return '';//invalid block of utf8
if($num<=0xFFFF) return chr(($num>>12)+224).chr((($num>>6)&63)+128).chr(($num&63)+128);
if($num<=0x10FFFF) return chr(($num>>18)+240).chr((($num>>12)&63)+128).chr((($num>>6)&63)+128).chr(($num&63)+128);
return '';
}
public static function is_valid_utf8($string)
{
for ($i=0, $ix=strlen($string); $i < $ix; $i++)
{
$c = ord($string[$i]);
if ($c==0x09 || $c==0x0a || $c==0x0d || (0x20 <= $c && $c < 0x7e) ) $n = 0; # 0bbbbbbb
else if (($c & 0xE0) == 0xC0) $n=1; # 110bbbbb
else if ($c==0xed && (ord($string[$i+1]) & 0xa0)==0xa0) return false; //code points, 0xd800 to 0xdfff
else if (($c & 0xF0) == 0xE0) $n=2; # 1110bbbb
else if (($c & 0xF8) == 0xF0) $n=3; # 11110bbb
//else if (($c & 0xFC) == 0xF8) $n=4; # 111110bb //byte 5, unnecessary in 4 byte UTF-8
//else if (($c & 0xFE) == 0xFC) $n=5; # 1111110b //byte 6, unnecessary in 4 byte UTF-8
else return false;
for ($j=0; $j<$n; $j++) { // n bytes matching 10bbbbbb follow ?
if ((++$i == $ix) || ((ord($string[$i]) & 0xC0) != 0x80))
return false;
}
}
return true;
}
}

32
lib/coupang.js Normal file
View File

@ -0,0 +1,32 @@
// coupang.js
// Namhyeon Go <abuse@catswords.net>
// https://github.com/gnh1201/welsonjs
//
// ***SECURITY NOTICE***
// Due to potential security issues, the Public API URL is not provided. If you need to request access, please refer to the project's contact information.
// You can download the server-side script that implements this functionality from the link below:
// https://github.com/gnh1201/caterpillar
//
var JsonRpc2 = require("lib/jsonrpc2");
function search(s) {
var rpc = JsonRpc2.create(JsonRpc2.DEFAULT_JSONRPC2_URL);
var result = rpc.invoke("relay_invoke_method", {
"callback": "load_script",
"requires": [
"https://pub-f926e14287b340cd9eff33731bb25329.r2.dev/coupang.class.php"
],
"args": [
"$search = new CoupangProductSearch(); return $search->searchProducts('" + s + "')"
]
}, "");
return result.data;
}
exports.search = search;
exports.VERSIONINFO = "Coupang Product Search (coupang.js) version 0.1";
exports.AUTHOR = "abuse@catswords.net";
exports.global = global;
exports.require = global.require;