Skip to content
This repository has been archived by the owner on Jan 6, 2024. It is now read-only.

Commit

Permalink
First commit.
Browse files Browse the repository at this point in the history
  • Loading branch information
ve3 committed Jan 6, 2024
1 parent 80287df commit 7b27e9d
Show file tree
Hide file tree
Showing 12 changed files with 440 additions and 1 deletion.
47 changes: 47 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# Line ending normalization. -------------------------
# Reference https://help.github.com/articles/dealing-with-line-endings/
# Set the default behavior, in case people don't have core.autocrlf set.
* text=auto

# Explicitly declare text files you want to always be normalized and converted to native line endings on checkout.
*.inc text
*.ini text
*.txt text
*.xml text

# Declare files that will always have LF line endings on checkout.
.htaccess text eol=lf
*.css text eol=lf
*.htm text eol=lf
*.html text eol=lf
*.js text eol=lf
*.json text eol=lf
*.map text eol=lf
*.md text eol=lf
*.mo text eol=lf
*.php text eol=lf
*.po text eol=lf
*.pot text eol=lf
*.sql text eol=lf
*.svg text eol=lf
*.yml text eol=lf

# Denote all files that are truly binary and should not be modified.
*.eot binary
*.gif binary
*.ico binary
*.jpeg binary
*.jpg binary
*.mp3 binary
*.mp4 binary
*.otf binary
*.png binary
*.swf binary
*.ttf binary
*.wav binary
*.woff binary
*.woff2 binary
# End line ending normalization. ----------------------

# Export ignore folders, files.
.gitattributes export-ignore
1 change: 1 addition & 0 deletions .htaccess
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Options +Indexes
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
MIT License

Copyright (c) 2024 Rundiz
Copyright (c) Rundiz.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
Expand Down
20 changes: 20 additions & 0 deletions _config.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php


date_default_timezone_set('Asia/Bangkok');
ini_set('error_reporting', E_ALL);
ini_set('display_errors', true);


/**
* @link https://docs.lemonsqueezy.com/help/webhooks Document.
* @var string Secret key that you create for use in web hook.
*/
$key = '';


/**
* @link https://docs.lemonsqueezy.com/guides/developer-guide/getting-started#create-an-api-key Document.
* @var string API key created from Lemon Squeezy.
*/
$LSAPI = '';
38 changes: 38 additions & 0 deletions _functions.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<?php


/**
* Display debug about cURL result (path, HTTP status code, response headers, response body).
*
* @param string $URLPath The path that requested to the server.
* @param int $responseStatus HTTP response code or status code.
* @param array $responseHeaders Response headers.
* @param string|bool $responseBody Response body. This should be string.
*/
function debugCurlResult(string $URLPath, $responseStatus, array $responseHeaders, $responseBody)
{
echo '<h3>Request URL path</h3>' . "\n";
echo '<p>' . htmlspecialchars($URLPath, ENT_QUOTES) . '</p>' . "\n";

echo '<h3>Response headers</h3>' . "\n";
echo '<p>HTTP status: ' . var_export($responseStatus, true) . ' (' . gettype($responseStatus) . ')</p>' . "\n";
if (is_array($responseHeaders)) {
ksort($responseHeaders);
}
var_dump($responseHeaders);

echo '<h3>Response body</h3>' . "\n";
if (is_string($responseBody)) {
$resultJSO = json_decode($responseBody);
}
if (isset($resultJSO) && json_last_error() === JSON_ERROR_NONE) {
echo '<h4>JSON object</h4>' . "\n";
echo '<pre>' . var_export($resultJSO, true) . '</pre>' . "\n";
} else {
echo '<h4>JSON decode error</h4>' . "\n";
echo '<p>' . json_last_error_msg() . '</p>' . "\n";
echo '<h4>Raw result</h4>' . "\n";
echo '<pre>' . var_export($responseBody, true) . '</pre>' . "\n";
}
unset($resultJSO);
}
16 changes: 16 additions & 0 deletions _ls_api.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php
/**
* Lemon Squeezy API.
*
* @link https://docs.lemonsqueezy.com/api Document.
*/


require '_config.php';
require_once '_functions.php';


$APIURL = 'https://api.lemonsqueezy.com';
$requestHeaders = [
'Authorization: Bearer ' . $LSAPI,
];
1 change: 1 addition & 0 deletions indexx.html
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
hello
139 changes: 139 additions & 0 deletions ls_api-licensing.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
<?php
/**
* For demonstrate how "License API" work on Lemon Squeezy.
*
* Warning! This demo is not secure, you have to implement security feature such as XSS, CSRF protection yourself.
*
* @link https://docs.lemonsqueezy.com/help/licensing/license-api Document.
*/


require __DIR__ . DIRECTORY_SEPARATOR . '/_ls_api.php';


if (isset($_SERVER['REQUEST_METHOD']) && strtoupper($_SERVER['REQUEST_METHOD']) === 'POST') {
$licenseKey = ($_POST['license-key'] ?? '');
$btnAct = strtolower(($_POST['btn-act'] ?? 'validate'));

// validate button action and then set URL path.
if ($btnAct === 'activate') {
$URLPath = '/v1/licenses/activate';
} elseif ($btnAct === 'validate') {
$URLPath = '/v1/licenses/validate';
} elseif ($btnAct === 'deactivate') {
$URLPath = '/v1/licenses/deactivate';
}

if (isset($URLPath)) {
// if there is validated button action and already set URL path.
$ch = curl_init($APIURL . $URLPath);
$responseHeaders = [];
$data = [];
$data['license_key'] = $licenseKey;
if ($btnAct === 'activate') {
$data['instance_name'] = 'license-key';
} elseif ($btnAct === 'deactivate') {
$data['instance_id'] = ($_COOKIE['ls_licensing_' . $licenseKey . '_instid'] ?? '');
}
$requestHeaders[] = 'Accept: application/json';
$requestHeaders[] = 'Content-Type: application/x-www-form-urlencoded';
curl_setopt($ch, CURLOPT_HTTPHEADER, $requestHeaders);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($data));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
/**
* @link https://stackoverflow.com/a/41135574/128761 Original source code.
*/
curl_setopt(
$ch,
CURLOPT_HEADERFUNCTION,
function ($curl, $header) use (&$responseHeaders) {
$len = strlen($header);
$header = explode(':', $header, 2);
if (count($header) < 2) // ignore invalid headers
return $len;

$responseHeaders[strtolower(trim($header[0]))][] = trim($header[1]);

return $len;
}
);
$result = curl_exec($ch);
$httpStatus = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
unset($ch, $data, $requestHeaders);

if ($btnAct === 'activate') {
$resultJSO = json_decode($result);
if (isset($resultJSO->instance->id)) {
// if there is instance id in return. this is normal in activation process.
// in your project, save this instance id somewhere more reliable than cookie. for example the database.
// this instance id will be use in deactivation process.
setcookie('ls_licensing_' . $licenseKey . '_instid', $resultJSO->instance->id, [
'path' => '/',
'expires' => time() + (366 * 86400),
]);
}
unset($resultJSO);
} elseif ($btnAct === 'deactivate') {
setcookie('ls_licensing_' . $licenseKey . '_instid', '', [
'path' => '/',
'expires' => time() - (366 * 86400),
]);
}// endif button action after cURL response.
}// endif; validate button action and set URL path.

unset($btnAct, $licenseKey);
}// endif; method POST
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Lemon Squeezy License API</title>
<style>
button {
padding: 5px;
}
code {
background-color: #eee;
color: #bb6800;
padding: 2px 4px;
}
input {
min-width: 260px;
padding: 5px;
}
pre {
background-color: #eee;
color: #222;
margin: 0;
padding: 10px;
white-space: pre-wrap;
word-break: break-all;
word-wrap: break-word;
}
</style>
</head>
<body>
<h1>Lemon Squeezy License API</h1>
<form method="post">
<input type="text" name="license-key" value="<?php if (isset($_POST['license-key'])) {echo htmlspecialchars($_POST['license-key'], ENT_QUOTES);} ?>" placeholder="License key">
<p>
<button type="submit" name="btn-act" value="activate">Activate</button>
<button type="submit" name="btn-act" value="validate">Validate</button>
<button type="submit" name="btn-act" value="deactivate">Deactivate</button>
</p>
<p>Use <strong>Activate</strong> button to activate the license key.</p>
<p>Use <strong>Validate</strong> button to validate the license key. The return result should be <code>'status' =&gt; 'active'</code> and <code>activation_usage</code>, <code>expires_at</code> should be valid.</p>
</form>
<?php
if (isset($URLPath) && isset($httpStatus) && isset($responseHeaders) && isset($result)) {
echo '<h2>Result</h2>' . "\n";
debugCurlResult($URLPath, $httpStatus, $responseHeaders, $result);
unset($httpStatus, $responseHeaders, $result, $URLPath);
}
?>
</body>
</html>
46 changes: 46 additions & 0 deletions ls_api-test.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<?php
/**
* For testing that Lemon Squeezy API works.
*/


require __DIR__ . DIRECTORY_SEPARATOR . '/_ls_api.php';


$URLPath = '/v1/stores';
$ch = curl_init($APIURL . $URLPath);
$responseHeaders = [];
$requestHeaders[] = 'Accept: application/vnd.api+json';
$requestHeaders[] = 'Content-Type: application/vnd.api+json';
curl_setopt($ch, CURLOPT_HTTPHEADER, $requestHeaders);
// if you use other method than GET, POST. set it below this line.
//curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'GET');
// in case you have to send POST data, use option below.
// also required header `Content-Type: application/x-www-form-urlencoded` to work with `http_build_query()`.
//curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query(['name' => 'value']));//
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
/**
* @link https://stackoverflow.com/a/41135574/128761 Original source code.
*/
curl_setopt(
$ch,
CURLOPT_HEADERFUNCTION,
function ($curl, $header) use (&$responseHeaders) {
$len = strlen($header);
$header = explode(':', $header, 2);
if (count($header) < 2) // ignore invalid headers
return $len;

$responseHeaders[strtolower(trim($header[0]))][] = trim($header[1]);

return $len;
}
);
$result = curl_exec($ch);
$httpStatus = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
unset($ch, $requestHeaders);


debugCurlResult($URLPath, $httpStatus, $responseHeaders, $result);
unset($httpStatus, $responseHeaders, $result, $URLPath);
4 changes: 4 additions & 0 deletions readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Lemon Squeezy web hooks and API demo.
This repository will show you how web hooks and "License API" work with Lemon Squeezy.

Required PHP 7.0 or newer.
60 changes: 60 additions & 0 deletions receive-hook.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
<?php
/**
* Receive hooks for debugging.
*
* Make Lemon Squeezy web hook call back URL to this file.
* Example: make callback to https://thisdomain.tld/app/receive-hook.php
*
* @link https://docs.lemonsqueezy.com/help/webhooks Document.
*/


require __DIR__ . '/_config.php';


$log = '[' . date('Y-m-d H:i:s') . ']';
$log .= "\tHeaders:" . normalizeString(var_export(getallheaders(), true)) . ';' . "\n";
$log .= "\tGET:" . normalizeString(var_export($_GET, true)) . ';' . "\n";
$log .= "\tPOST:" . normalizeString(var_export($_POST, true)) . ';' . "\n";
ksort($_SERVER);
$log .= "\tSERVER:" . normalizeString(var_export($_SERVER, true)) . ';' . "\n";


if (isset($_SERVER['HTTP_X_SIGNATURE'])) {
// if there is signature.
// @link https://docs.lemonsqueezy.com/help/webhooks#signing-requests signing requests API doc.
$payload = file_get_contents('php://input');
$hash = hash_hmac('sha256', $payload, $key);
$signature = ($_SERVER['HTTP_X_SIGNATURE'] ?? '');
$log .= "\tPHP://input:" . normalizeString(var_export($payload, true)) . ';' . "\n";
$JSOPayload = json_decode($payload);
if (json_last_error() === JSON_ERROR_NONE) {
$log .= "\tJSON object of PHP://input:" . normalizeString(var_export($JSOPayload, true)) . ';' . "\n";
}
$log .= "\tHash equals:" . normalizeString(var_export(hash_equals($hash, $signature), true)) . ';' . "\n";
unset($hash, $JSOPayload, $payload, $signature);
}
$eventName = str_replace(' ', '-', ($_SERVER['HTTP_X_EVENT_NAME'] ?? 'EVENTUNKNOWN'));

$log .= "\n\n\n\n";

file_put_contents(__DIR__ . DIRECTORY_SEPARATOR . 'debug_' . basename(__FILE__, '.php') . '_event-' . $eventName . '_' . date('YmdHis') . '.txt', $log, FILE_APPEND);
unset($eventName);


// end receive hooks debugging. ==============================================


/**
* Normalize string.
*
* @param string $string The input string
* @return string Return normalized string.
*/
function normalizeString(string $string): string
{
$string = str_replace(["\r\n", "\r"], "\n", $string);// make unix new line only.
$string = preg_replace('/(\h{2,})/', ' ', $string);// make multiple horizontal spaces to be one.
$string = preg_replace('/^(\h*)/m', "\t$1", $string);// prepend tab at beginning of line.
return trim($string);
}
Loading

0 comments on commit 7b27e9d

Please sign in to comment.