save
This commit is contained in:
parent
9b15ac9fd3
commit
27df1a73b5
|
@ -11,9 +11,3 @@ SMTP_FROM="noreply@example.com"
|
|||
# !! Choose your LN_SERVICE carefully!!
|
||||
# NOTE: The LN_SERVICE must support LUD-21 Payment Verification
|
||||
LN_ADDRESS="your@node.win"
|
||||
# maps.co for GeoCoder (postal address verification)
|
||||
# Get your free API key: https://geocode.maps.co
|
||||
GEOCODE_MAPS_CO_API_KEY="your-api-key-from-geocode.maps.co"
|
||||
# Plausible for privacy-respecting page analytics
|
||||
# https://github.com/plausible/analytics
|
||||
PLAUSIBLE_HOST="https://plausible.io/"
|
|
@ -10,6 +10,7 @@
|
|||
"phpmailer/phpmailer": "^6.9.2",
|
||||
"vlucas/phpdotenv": "^5.6",
|
||||
"web-auth/webauthn-lib": "^5.0",
|
||||
"twig/twig": "^3.0"
|
||||
"twig/twig": "^3.0",
|
||||
"swentel/nostr-php": "^1.5"
|
||||
}
|
||||
}
|
||||
|
|
825
composer.lock
generated
825
composer.lock
generated
|
@ -4,8 +4,54 @@
|
|||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "ea128e458544f87060ad53880489fcc2",
|
||||
"content-hash": "408b4a1daa73232eabf14c566a3e5d8d",
|
||||
"packages": [
|
||||
{
|
||||
"name": "bitwasp/bech32",
|
||||
"version": "v0.0.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/Bit-Wasp/bech32.git",
|
||||
"reference": "e1ea58c848a4ec59d81b697b3dfe9cc99968d0e7"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/Bit-Wasp/bech32/zipball/e1ea58c848a4ec59d81b697b3dfe9cc99968d0e7",
|
||||
"reference": "e1ea58c848a4ec59d81b697b3dfe9cc99968d0e7",
|
||||
"shasum": ""
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^5.4.0",
|
||||
"squizlabs/php_codesniffer": "^2.0.0"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"files": [
|
||||
"src/bech32.php"
|
||||
],
|
||||
"psr-4": {
|
||||
"BitWasp\\Bech32\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"Unlicense"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Thomas Kerin",
|
||||
"homepage": "https://thomaskerin.io",
|
||||
"role": "Author"
|
||||
}
|
||||
],
|
||||
"description": "Pure (no dependencies) implementation of bech32",
|
||||
"homepage": "https://github.com/bit-wasp/bech32",
|
||||
"support": {
|
||||
"issues": "https://github.com/Bit-Wasp/bech32/issues",
|
||||
"source": "https://github.com/Bit-Wasp/bech32/tree/more-tests"
|
||||
},
|
||||
"time": "2018-02-05T22:23:47+00:00"
|
||||
},
|
||||
{
|
||||
"name": "brick/math",
|
||||
"version": "0.12.1",
|
||||
|
@ -618,6 +664,239 @@
|
|||
},
|
||||
"time": "2024-10-13T11:29:49+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phrity/net-stream",
|
||||
"version": "2.1.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sirn-se/phrity-net-stream.git",
|
||||
"reference": "e6ace997168bebcce814c95cd5c78c78663ae49a"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/sirn-se/phrity-net-stream/zipball/e6ace997168bebcce814c95cd5c78c78663ae49a",
|
||||
"reference": "e6ace997168bebcce814c95cd5c78c78663ae49a",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^8.0",
|
||||
"phrity/util-errorhandler": "^1.1",
|
||||
"psr/http-factory": "^1.0",
|
||||
"psr/http-message": "^1.1 | ^2.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"php-coveralls/php-coveralls": "^2.0",
|
||||
"phpunit/phpunit": "^9.0 | ^10.0 | ^11.0",
|
||||
"phrity/net-uri": "^2.0",
|
||||
"squizlabs/php_codesniffer": "^3.5"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Phrity\\Net\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Sören Jensen",
|
||||
"email": "sirn@sirn.se",
|
||||
"homepage": "https://phrity.sirn.se"
|
||||
}
|
||||
],
|
||||
"description": "Socket stream classes implementing PSR-7 Stream and PSR-17 StreamFactory",
|
||||
"homepage": "https://phrity.sirn.se/net-stream",
|
||||
"keywords": [
|
||||
"Socket",
|
||||
"client",
|
||||
"psr-17",
|
||||
"psr-7",
|
||||
"server",
|
||||
"stream",
|
||||
"stream factory"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/sirn-se/phrity-net-stream/issues",
|
||||
"source": "https://github.com/sirn-se/phrity-net-stream/tree/2.1.2"
|
||||
},
|
||||
"time": "2025-01-09T08:07:34+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phrity/net-uri",
|
||||
"version": "2.1.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sirn-se/phrity-net-uri.git",
|
||||
"reference": "841190135af4fab18135226aaaf99ec5791377ac"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/sirn-se/phrity-net-uri/zipball/841190135af4fab18135226aaaf99ec5791377ac",
|
||||
"reference": "841190135af4fab18135226aaaf99ec5791377ac",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-mbstring": "*",
|
||||
"php": "^8.0",
|
||||
"psr/http-factory": "^1.0",
|
||||
"psr/http-message": "^1.1 | ^2.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"php-coveralls/php-coveralls": "^2.0",
|
||||
"phpunit/phpunit": "^9.0 | ^10.0 | ^11.0",
|
||||
"phrity/util-errorhandler": "^1.1",
|
||||
"squizlabs/php_codesniffer": "^3.5"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-intl": "Enables IDN conversion for non-ASCII domains"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Phrity\\Net\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Sören Jensen",
|
||||
"email": "sirn@sirn.se",
|
||||
"homepage": "https://phrity.sirn.se"
|
||||
}
|
||||
],
|
||||
"description": "PSR-7 Uri and PSR-17 UriFactory implementation",
|
||||
"homepage": "https://phrity.sirn.se/net-uri",
|
||||
"keywords": [
|
||||
"psr-17",
|
||||
"psr-7",
|
||||
"uri",
|
||||
"uri factory"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/sirn-se/phrity-net-uri/issues",
|
||||
"source": "https://github.com/sirn-se/phrity-net-uri/tree/2.1.0"
|
||||
},
|
||||
"time": "2024-07-08T06:14:09+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phrity/util-errorhandler",
|
||||
"version": "1.1.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sirn-se/phrity-util-errorhandler.git",
|
||||
"reference": "483228156e06673963902b1cc1e6bd9541ab4d5e"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/sirn-se/phrity-util-errorhandler/zipball/483228156e06673963902b1cc1e6bd9541ab4d5e",
|
||||
"reference": "483228156e06673963902b1cc1e6bd9541ab4d5e",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.4 | ^8.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"php-coveralls/php-coveralls": "^2.0",
|
||||
"phpunit/phpunit": "^9.0 | ^10.0 | ^11.0",
|
||||
"squizlabs/php_codesniffer": "^3.5"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Phrity\\Util\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Sören Jensen",
|
||||
"email": "sirn@sirn.se",
|
||||
"homepage": "https://phrity.sirn.se"
|
||||
}
|
||||
],
|
||||
"description": "Inline error handler; catch and resolve errors for code block.",
|
||||
"homepage": "https://phrity.sirn.se/util-errorhandler",
|
||||
"keywords": [
|
||||
"error",
|
||||
"warning"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/sirn-se/phrity-util-errorhandler/issues",
|
||||
"source": "https://github.com/sirn-se/phrity-util-errorhandler/tree/1.1.1"
|
||||
},
|
||||
"time": "2024-09-12T06:49:16+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phrity/websocket",
|
||||
"version": "3.2.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sirn-se/websocket-php.git",
|
||||
"reference": "6dceb42be8bfd500806a5508721ae14cf0d4d737"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/sirn-se/websocket-php/zipball/6dceb42be8bfd500806a5508721ae14cf0d4d737",
|
||||
"reference": "6dceb42be8bfd500806a5508721ae14cf0d4d737",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^8.1",
|
||||
"phrity/net-stream": "^2.1",
|
||||
"phrity/net-uri": "^2.1",
|
||||
"psr/http-message": "^1.1 | ^2.0",
|
||||
"psr/log": "^1.0 | ^2.0 | ^3.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"php-coveralls/php-coveralls": "^2.0",
|
||||
"phpstan/phpstan": "^2.0",
|
||||
"phpunit/phpunit": "^10.0 | ^11.0",
|
||||
"phrity/net-mock": "^2.1",
|
||||
"phrity/util-errorhandler": "^1.1",
|
||||
"squizlabs/php_codesniffer": "^3.5"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"WebSocket\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"ISC"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Fredrik Liljegren"
|
||||
},
|
||||
{
|
||||
"name": "Sören Jensen",
|
||||
"email": "sirn@sirn.se",
|
||||
"homepage": "https://phrity.sirn.se"
|
||||
}
|
||||
],
|
||||
"description": "WebSocket client and server",
|
||||
"homepage": "https://phrity.sirn.se/websocket",
|
||||
"keywords": [
|
||||
"client",
|
||||
"server",
|
||||
"websocket"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/sirn-se/websocket-php/issues",
|
||||
"source": "https://github.com/sirn-se/websocket-php/tree/3.2.2"
|
||||
},
|
||||
"time": "2025-01-10T09:41:26+00:00"
|
||||
},
|
||||
{
|
||||
"name": "psr/clock",
|
||||
"version": "1.0.0",
|
||||
|
@ -769,6 +1048,114 @@
|
|||
},
|
||||
"time": "2019-01-08T18:20:26+00:00"
|
||||
},
|
||||
{
|
||||
"name": "psr/http-factory",
|
||||
"version": "1.1.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/php-fig/http-factory.git",
|
||||
"reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/php-fig/http-factory/zipball/2b4765fddfe3b508ac62f829e852b1501d3f6e8a",
|
||||
"reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.1",
|
||||
"psr/http-message": "^1.0 || ^2.0"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.0.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Psr\\Http\\Message\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "PHP-FIG",
|
||||
"homepage": "https://www.php-fig.org/"
|
||||
}
|
||||
],
|
||||
"description": "PSR-17: Common interfaces for PSR-7 HTTP message factories",
|
||||
"keywords": [
|
||||
"factory",
|
||||
"http",
|
||||
"message",
|
||||
"psr",
|
||||
"psr-17",
|
||||
"psr-7",
|
||||
"request",
|
||||
"response"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/php-fig/http-factory"
|
||||
},
|
||||
"time": "2024-04-15T12:06:14+00:00"
|
||||
},
|
||||
{
|
||||
"name": "psr/http-message",
|
||||
"version": "2.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/php-fig/http-message.git",
|
||||
"reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/php-fig/http-message/zipball/402d35bcb92c70c026d1a6a9883f06b2ead23d71",
|
||||
"reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.2 || ^8.0"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "2.0.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Psr\\Http\\Message\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "PHP-FIG",
|
||||
"homepage": "https://www.php-fig.org/"
|
||||
}
|
||||
],
|
||||
"description": "Common interface for HTTP messages",
|
||||
"homepage": "https://github.com/php-fig/http-message",
|
||||
"keywords": [
|
||||
"http",
|
||||
"http-message",
|
||||
"psr",
|
||||
"psr-7",
|
||||
"request",
|
||||
"response"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/php-fig/http-message/tree/2.0"
|
||||
},
|
||||
"time": "2023-04-04T09:54:51+00:00"
|
||||
},
|
||||
{
|
||||
"name": "psr/log",
|
||||
"version": "3.0.2",
|
||||
|
@ -819,6 +1206,153 @@
|
|||
},
|
||||
"time": "2024-09-11T13:17:53+00:00"
|
||||
},
|
||||
{
|
||||
"name": "simplito/bigint-wrapper-php",
|
||||
"version": "1.0.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/simplito/bigint-wrapper-php.git",
|
||||
"reference": "cf21ec76d33f103add487b3eadbd9f5033a25930"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/simplito/bigint-wrapper-php/zipball/cf21ec76d33f103add487b3eadbd9f5033a25930",
|
||||
"reference": "cf21ec76d33f103add487b3eadbd9f5033a25930",
|
||||
"shasum": ""
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"BI\\": "lib/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Simplito Team",
|
||||
"email": "s.smyczynski@simplito.com",
|
||||
"homepage": "https://simplito.com"
|
||||
}
|
||||
],
|
||||
"description": "Common interface for php_gmp and php_bcmath modules",
|
||||
"support": {
|
||||
"issues": "https://github.com/simplito/bigint-wrapper-php/issues",
|
||||
"source": "https://github.com/simplito/bigint-wrapper-php/tree/1.0.0"
|
||||
},
|
||||
"time": "2018-02-27T12:38:08+00:00"
|
||||
},
|
||||
{
|
||||
"name": "simplito/bn-php",
|
||||
"version": "1.1.4",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/simplito/bn-php.git",
|
||||
"reference": "83446756a81720eacc2ffb87ff97958431451fd6"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/simplito/bn-php/zipball/83446756a81720eacc2ffb87ff97958431451fd6",
|
||||
"reference": "83446756a81720eacc2ffb87ff97958431451fd6",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"simplito/bigint-wrapper-php": "~1.0.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "*"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"BN\\": "lib/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Simplito Team",
|
||||
"email": "s.smyczynski@simplito.com",
|
||||
"homepage": "https://simplito.com"
|
||||
}
|
||||
],
|
||||
"description": "Big number implementation compatible with bn.js",
|
||||
"support": {
|
||||
"issues": "https://github.com/simplito/bn-php/issues",
|
||||
"source": "https://github.com/simplito/bn-php/tree/1.1.4"
|
||||
},
|
||||
"time": "2024-01-10T16:16:59+00:00"
|
||||
},
|
||||
{
|
||||
"name": "simplito/elliptic-php",
|
||||
"version": "1.0.12",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/simplito/elliptic-php.git",
|
||||
"reference": "be321666781be2be2c89c79c43ffcac834bc8868"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/simplito/elliptic-php/zipball/be321666781be2be2c89c79c43ffcac834bc8868",
|
||||
"reference": "be321666781be2be2c89c79c43ffcac834bc8868",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-gmp": "*",
|
||||
"simplito/bn-php": "~1.1.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpbench/phpbench": "@dev",
|
||||
"phpunit/phpunit": "*"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Elliptic\\": "lib/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Simplito Team",
|
||||
"email": "s.smyczynski@simplito.com",
|
||||
"homepage": "https://simplito.com"
|
||||
}
|
||||
],
|
||||
"description": "Fast elliptic curve cryptography",
|
||||
"homepage": "https://github.com/simplito/elliptic-php",
|
||||
"keywords": [
|
||||
"Curve25519",
|
||||
"ECDSA",
|
||||
"Ed25519",
|
||||
"EdDSA",
|
||||
"cryptography",
|
||||
"curve",
|
||||
"curve25519-weier",
|
||||
"ecc",
|
||||
"ecdh",
|
||||
"elliptic",
|
||||
"nistp192",
|
||||
"nistp224",
|
||||
"nistp256",
|
||||
"nistp384",
|
||||
"nistp521",
|
||||
"secp256k1"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/simplito/elliptic-php/issues",
|
||||
"source": "https://github.com/simplito/elliptic-php/tree/1.0.12"
|
||||
},
|
||||
"time": "2024-01-09T14:57:04+00:00"
|
||||
},
|
||||
{
|
||||
"name": "spomky-labs/cbor-php",
|
||||
"version": "3.1.0",
|
||||
|
@ -1011,6 +1545,73 @@
|
|||
],
|
||||
"time": "2025-01-03T09:35:48+00:00"
|
||||
},
|
||||
{
|
||||
"name": "swentel/nostr-php",
|
||||
"version": "1.5.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/nostrver-se/nostr-php.git",
|
||||
"reference": "6ef1e05da3845e352593c7d119fc00e716b3321f"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/nostrver-se/nostr-php/zipball/6ef1e05da3845e352593c7d119fc00e716b3321f",
|
||||
"reference": "6ef1e05da3845e352593c7d119fc00e716b3321f",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"bitwasp/bech32": "^0.0.1",
|
||||
"ext-gmp": "*",
|
||||
"ext-xml": "*",
|
||||
"php": ">=8.1 <8.5",
|
||||
"phrity/websocket": "^3.0",
|
||||
"simplito/elliptic-php": "^1.0",
|
||||
"uma/phpecc": "^0.2.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"friendsofphp/php-cs-fixer": "^3.51",
|
||||
"phpunit/phpunit": "^10.5"
|
||||
},
|
||||
"bin": [
|
||||
"bin/nostr-php"
|
||||
],
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"swentel\\nostr\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Sebastian Hagens",
|
||||
"email": "info@sebastix.nl",
|
||||
"homepage": "https://sebastix.nl",
|
||||
"role": "Developer & maintainer"
|
||||
},
|
||||
{
|
||||
"name": "Kristof De Jaeger",
|
||||
"homepage": "https://realize.be",
|
||||
"role": "Original author"
|
||||
}
|
||||
],
|
||||
"description": "Nostr helper library for PHP",
|
||||
"homepage": "https://nostr-php.dev",
|
||||
"keywords": [
|
||||
"library",
|
||||
"nostr"
|
||||
],
|
||||
"support": {
|
||||
"chat": "https://t.me/nostr_php",
|
||||
"issue": "https://github.com/swentel/nostr-php/issues",
|
||||
"issues": "https://github.com/nostrver-se/nostr-php/issues",
|
||||
"source": "https://github.com/nostrver-se/nostr-php/tree/push"
|
||||
},
|
||||
"time": "2025-01-04T21:46:58+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/clock",
|
||||
"version": "v7.2.0",
|
||||
|
@ -1783,16 +2384,16 @@
|
|||
},
|
||||
{
|
||||
"name": "symfony/property-access",
|
||||
"version": "v7.2.0",
|
||||
"version": "v7.2.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/property-access.git",
|
||||
"reference": "3ae42efba01e45aaedecf5c93c8d6a3ab3a82276"
|
||||
"reference": "b28732e315d81fbec787f838034de7d6c9b2b902"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/property-access/zipball/3ae42efba01e45aaedecf5c93c8d6a3ab3a82276",
|
||||
"reference": "3ae42efba01e45aaedecf5c93c8d6a3ab3a82276",
|
||||
"url": "https://api.github.com/repos/symfony/property-access/zipball/b28732e315d81fbec787f838034de7d6c9b2b902",
|
||||
"reference": "b28732e315d81fbec787f838034de7d6c9b2b902",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -1839,7 +2440,7 @@
|
|||
"reflection"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/property-access/tree/v7.2.0"
|
||||
"source": "https://github.com/symfony/property-access/tree/v7.2.3"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
@ -1855,20 +2456,20 @@
|
|||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2024-09-26T12:28:35+00:00"
|
||||
"time": "2025-01-17T10:56:55+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/property-info",
|
||||
"version": "v7.2.2",
|
||||
"version": "v7.2.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/property-info.git",
|
||||
"reference": "1dfeb0dac7a99f7b3be42db9ccc299c5a6483fcf"
|
||||
"reference": "dedb118fd588a92f226b390250b384d25f4192fe"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/property-info/zipball/1dfeb0dac7a99f7b3be42db9ccc299c5a6483fcf",
|
||||
"reference": "1dfeb0dac7a99f7b3be42db9ccc299c5a6483fcf",
|
||||
"url": "https://api.github.com/repos/symfony/property-info/zipball/dedb118fd588a92f226b390250b384d25f4192fe",
|
||||
"reference": "dedb118fd588a92f226b390250b384d25f4192fe",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -1879,7 +2480,9 @@
|
|||
"conflict": {
|
||||
"phpdocumentor/reflection-docblock": "<5.2",
|
||||
"phpdocumentor/type-resolver": "<1.5.1",
|
||||
"symfony/dependency-injection": "<6.4"
|
||||
"symfony/cache": "<6.4",
|
||||
"symfony/dependency-injection": "<6.4",
|
||||
"symfony/serializer": "<6.4"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpdocumentor/reflection-docblock": "^5.2",
|
||||
|
@ -1922,7 +2525,7 @@
|
|||
"validator"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/property-info/tree/v7.2.2"
|
||||
"source": "https://github.com/symfony/property-info/tree/v7.2.3"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
@ -1938,20 +2541,20 @@
|
|||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2024-12-31T11:04:50+00:00"
|
||||
"time": "2025-01-27T11:08:17+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/serializer",
|
||||
"version": "v7.2.0",
|
||||
"version": "v7.2.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/serializer.git",
|
||||
"reference": "3f5ed9f5e6c02e3853109190ba38408f5e1d2dd0"
|
||||
"reference": "320f30beb419ce4f96363ada5e225c41f1ef08ab"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/serializer/zipball/3f5ed9f5e6c02e3853109190ba38408f5e1d2dd0",
|
||||
"reference": "3f5ed9f5e6c02e3853109190ba38408f5e1d2dd0",
|
||||
"url": "https://api.github.com/repos/symfony/serializer/zipball/320f30beb419ce4f96363ada5e225c41f1ef08ab",
|
||||
"reference": "320f30beb419ce4f96363ada5e225c41f1ef08ab",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -2020,7 +2623,7 @@
|
|||
"description": "Handles serializing and deserializing data structures, including object graphs, into array structures or other formats like XML and JSON.",
|
||||
"homepage": "https://symfony.com",
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/serializer/tree/v7.2.0"
|
||||
"source": "https://github.com/symfony/serializer/tree/v7.2.3"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
@ -2036,7 +2639,7 @@
|
|||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2024-11-25T15:21:05+00:00"
|
||||
"time": "2025-01-29T07:13:55+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/string",
|
||||
|
@ -2276,16 +2879,16 @@
|
|||
},
|
||||
{
|
||||
"name": "twig/twig",
|
||||
"version": "v3.18.0",
|
||||
"version": "v3.19.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/twigphp/Twig.git",
|
||||
"reference": "acffa88cc2b40dbe42eaf3a5025d6c0d4600cc50"
|
||||
"reference": "d4f8c2b86374f08efc859323dbcd95c590f7124e"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/twigphp/Twig/zipball/acffa88cc2b40dbe42eaf3a5025d6c0d4600cc50",
|
||||
"reference": "acffa88cc2b40dbe42eaf3a5025d6c0d4600cc50",
|
||||
"url": "https://api.github.com/repos/twigphp/Twig/zipball/d4f8c2b86374f08efc859323dbcd95c590f7124e",
|
||||
"reference": "d4f8c2b86374f08efc859323dbcd95c590f7124e",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -2340,7 +2943,7 @@
|
|||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/twigphp/Twig/issues",
|
||||
"source": "https://github.com/twigphp/Twig/tree/v3.18.0"
|
||||
"source": "https://github.com/twigphp/Twig/tree/v3.19.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
@ -2352,7 +2955,165 @@
|
|||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2024-12-29T10:51:50+00:00"
|
||||
"time": "2025-01-29T07:06:14+00:00"
|
||||
},
|
||||
{
|
||||
"name": "uma/phpasn1",
|
||||
"version": "v2.5.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/1ma/PHPASN1.git",
|
||||
"reference": "dd805d3157ddc90515ee13562a9bb241c68f4f88"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/1ma/PHPASN1/zipball/dd805d3157ddc90515ee13562a9bb241c68f4f88",
|
||||
"reference": "dd805d3157ddc90515ee13562a9bb241c68f4f88",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^8.1"
|
||||
},
|
||||
"require-dev": {
|
||||
"php-coveralls/php-coveralls": "^2.0",
|
||||
"phpunit/phpunit": "^9.6"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-bcmath": "BCmath is the fallback extension for big integer calculations",
|
||||
"ext-curl": "For loading OID information from the web if they have not bee defined statically",
|
||||
"ext-gmp": "GMP is the preferred extension for big integer calculations",
|
||||
"phpseclib/bcmath_compat": "BCmath polyfill for servers where neither GMP nor BCmath is available"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "2.0.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"FG\\": "lib/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Friedrich Große",
|
||||
"email": "friedrich.grosse@gmail.com",
|
||||
"homepage": "https://github.com/FGrosse",
|
||||
"role": "Author"
|
||||
},
|
||||
{
|
||||
"name": "All contributors",
|
||||
"homepage": "https://github.com/FGrosse/PHPASN1/contributors"
|
||||
}
|
||||
],
|
||||
"description": "A PHP Framework that allows you to encode and decode arbitrary ASN.1 structures using the ITU-T X.690 Encoding Rules.",
|
||||
"homepage": "https://github.com/FGrosse/PHPASN1",
|
||||
"keywords": [
|
||||
"DER",
|
||||
"asn.1",
|
||||
"asn1",
|
||||
"ber",
|
||||
"binary",
|
||||
"decoding",
|
||||
"encoding",
|
||||
"x.509",
|
||||
"x.690",
|
||||
"x509",
|
||||
"x690"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/1ma/PHPASN1/tree/v2.5.1"
|
||||
},
|
||||
"abandoned": "genkgo/php-asn1",
|
||||
"time": "2024-11-29T17:34:36+00:00"
|
||||
},
|
||||
{
|
||||
"name": "uma/phpecc",
|
||||
"version": "v0.2.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/1ma/phpecc.git",
|
||||
"reference": "e269be5eaef099fb46831037d097c647a6da2f69"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/1ma/phpecc/zipball/e269be5eaef099fb46831037d097c647a6da2f69",
|
||||
"reference": "e269be5eaef099fb46831037d097c647a6da2f69",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-gmp": "*",
|
||||
"php": "^8.0",
|
||||
"uma/phpasn1": "^2.5.1"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^9.0",
|
||||
"squizlabs/php_codesniffer": "^2.0",
|
||||
"symfony/yaml": "^6.4 || ^7.0"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Mdanter\\Ecc\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Matyas Danter",
|
||||
"homepage": "http://matejdanter.com/",
|
||||
"role": "Author"
|
||||
},
|
||||
{
|
||||
"name": "Thibaud Fabre",
|
||||
"email": "thibaud@aztech.io",
|
||||
"homepage": "http://aztech.io",
|
||||
"role": "Maintainer"
|
||||
},
|
||||
{
|
||||
"name": "Thomas Kerin",
|
||||
"email": "afk11@users.noreply.github.com",
|
||||
"role": "Maintainer"
|
||||
},
|
||||
{
|
||||
"name": "Ology Newswire, Inc",
|
||||
"email": "protocol@vpsqr.com",
|
||||
"homepage": "https://vpsqr.com/",
|
||||
"role": "Maintainer"
|
||||
}
|
||||
],
|
||||
"description": "Temporary fork of public-square/phpecc",
|
||||
"homepage": "https://github.com/1ma/phpecc",
|
||||
"keywords": [
|
||||
"Diffie",
|
||||
"ECDSA",
|
||||
"Hellman",
|
||||
"curve",
|
||||
"ecdh",
|
||||
"elliptic",
|
||||
"nistp192",
|
||||
"nistp224",
|
||||
"nistp256",
|
||||
"nistp384",
|
||||
"nistp521",
|
||||
"phpecc",
|
||||
"schnorr",
|
||||
"secp256k1",
|
||||
"secp256r1"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/1ma/phpecc/tree/v0.2.1"
|
||||
},
|
||||
"abandoned": "paragonie/ecc",
|
||||
"time": "2025-01-21T00:38:50+00:00"
|
||||
},
|
||||
{
|
||||
"name": "vlucas/phpdotenv",
|
||||
|
@ -2522,16 +3283,16 @@
|
|||
},
|
||||
{
|
||||
"name": "web-auth/webauthn-lib",
|
||||
"version": "5.0.1",
|
||||
"version": "5.1.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/web-auth/webauthn-lib.git",
|
||||
"reference": "2cc8262b885cf01eee3c4c10ca3985bdd2614c97"
|
||||
"reference": "6b95b2b3902d943796c3c2bac2dd14af9d031fc2"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/web-auth/webauthn-lib/zipball/2cc8262b885cf01eee3c4c10ca3985bdd2614c97",
|
||||
"reference": "2cc8262b885cf01eee3c4c10ca3985bdd2614c97",
|
||||
"url": "https://api.github.com/repos/web-auth/webauthn-lib/zipball/6b95b2b3902d943796c3c2bac2dd14af9d031fc2",
|
||||
"reference": "6b95b2b3902d943796c3c2bac2dd14af9d031fc2",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -2592,7 +3353,7 @@
|
|||
"webauthn"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/web-auth/webauthn-lib/tree/5.0.1"
|
||||
"source": "https://github.com/web-auth/webauthn-lib/tree/5.1.1"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
@ -2604,7 +3365,7 @@
|
|||
"type": "patreon"
|
||||
}
|
||||
],
|
||||
"time": "2024-07-20T05:24:59+00:00"
|
||||
"time": "2025-01-03T23:01:20+00:00"
|
||||
},
|
||||
{
|
||||
"name": "webmozart/assert",
|
||||
|
|
|
@ -21,19 +21,31 @@ Dotenv\Dotenv::createImmutable(__DIR__ . '/../')->load();
|
|||
// Start the session
|
||||
app::init_db();
|
||||
use app\models\addresses;
|
||||
use app\models\cart_items;
|
||||
use app\models\carts;
|
||||
use app\models\magic_links;
|
||||
use app\models\order_items;
|
||||
use app\models\orders;
|
||||
use app\models\products;
|
||||
use app\models\quote_items;
|
||||
use app\models\quotes;
|
||||
use app\models\subscriptions;
|
||||
use app\models\transactions;
|
||||
use app\models\user_addresses;
|
||||
use app\models\users;
|
||||
|
||||
if (!app::$db->query("SELECT name FROM sqlite_master WHERE type='table' AND name='users'")->fetch()) {
|
||||
addresses::init();
|
||||
cart_items::init();
|
||||
carts::init();
|
||||
magic_links::init();
|
||||
order_items::init();
|
||||
orders::init();
|
||||
products::init();
|
||||
quote_items::init();
|
||||
quotes::init();
|
||||
subscriptions::init();
|
||||
transactions::init();
|
||||
user_addresses::init();
|
||||
users::init();
|
||||
}
|
||||
|
@ -140,6 +152,7 @@ $controller = match ($route) {
|
|||
'/account' => account::index($defaults),
|
||||
'/account/profile' => account::profile(),
|
||||
'/account/login' => account::login($defaults),
|
||||
'/account/email' => account::email(),
|
||||
'/account/logout' => account::logout(),
|
||||
'/magic-link' => magic_link::index(),
|
||||
'/account/returns' => account::returns($defaults),
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
<?php
|
||||
namespace app;
|
||||
// for email
|
||||
use PHPMailer\PHPMailer\PHPMailer;
|
||||
use PHPMailer\PHPMailer\SMTP;
|
||||
use PHPMailer\PHPMailer\Exception;
|
||||
|
@ -7,6 +8,7 @@ use PHPMailer\PHPMailer\Exception;
|
|||
class app
|
||||
{
|
||||
public static $db;
|
||||
|
||||
public static function init_db()
|
||||
{
|
||||
try {
|
||||
|
@ -16,6 +18,7 @@ class app
|
|||
die("Database error: " . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public static function send_mail($to, $from, $from_name, $subject, $message, $HTML_message)
|
||||
{
|
||||
$mail = new PHPMailer(exceptions: true);
|
||||
|
@ -40,6 +43,7 @@ class app
|
|||
$mail->send();
|
||||
ob_end_clean();
|
||||
}
|
||||
|
||||
public static function sendJson($data, $status = 200)
|
||||
{
|
||||
http_response_code($status);
|
||||
|
|
|
@ -4,6 +4,7 @@ namespace app\controllers;
|
|||
use app\models\addresses;
|
||||
use app\models\users;
|
||||
use app\models\user_addresses;
|
||||
use app\models\magic_links;
|
||||
|
||||
class account
|
||||
{
|
||||
|
@ -12,8 +13,8 @@ class account
|
|||
if (!isset($_SESSION['user_id'])) {
|
||||
header('Location: /account/login');
|
||||
}
|
||||
$email = $_SESSION['user_email'];
|
||||
$user = users::getByEmail($email);
|
||||
$user_id = $_SESSION['user_id'];
|
||||
$user = users::getById($user_id);
|
||||
$default_shipping = null;
|
||||
$default_billing = null;
|
||||
$ship_addrs = [];
|
||||
|
@ -60,8 +61,8 @@ class account
|
|||
$bill_id = addresses::add(
|
||||
$bill['name'],
|
||||
$bill['company'],
|
||||
$bill['street'],
|
||||
$bill['boxapt'],
|
||||
$bill['addressLine1'],
|
||||
$bill['addressLine2'],
|
||||
$bill['city'],
|
||||
$bill['state'],
|
||||
$bill['zip'],
|
||||
|
@ -76,8 +77,8 @@ class account
|
|||
$_SESSION['success'] = "Billing address saved!";
|
||||
header('Location: /account/billing');
|
||||
}
|
||||
$email = $_SESSION['user_email'];
|
||||
$user = users::getByEmail($email);
|
||||
$user_id = $_SESSION['user_id'];
|
||||
$user = users::getById($user_id);
|
||||
$default_billing = null;
|
||||
$bill_addrs = [];
|
||||
$bill_addresses = user_addresses::getBillingByUserId($_SESSION['user_id']);
|
||||
|
@ -115,8 +116,42 @@ class account
|
|||
header('Location: /account');
|
||||
}
|
||||
}
|
||||
|
||||
public static function email()
|
||||
{
|
||||
$user_id = $_SESSION['user_id'] ?? null;
|
||||
if (empty($user_id)){
|
||||
header('Location: /account/login');
|
||||
}
|
||||
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
|
||||
$email = $_POST['email'] ?? null;
|
||||
if (empty($email)) {
|
||||
$_SESSION['error'] = "Enter your email to get a login link";
|
||||
header('Location: /account');
|
||||
exit;
|
||||
} else {
|
||||
$token = magic_links::add($email, $user_id);
|
||||
users::updateReplaceEmailTokenById($user_id, $token);
|
||||
header('Location: /account');
|
||||
exit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static function login($defaults)
|
||||
{
|
||||
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
|
||||
$email = $_POST['email'] ?? false;
|
||||
if (empty($email)) {
|
||||
$_SESSION['error'] = "Enter your email to get a login link";
|
||||
header('Location: /account/login');
|
||||
exit;
|
||||
} else {
|
||||
$token = magic_links::add($email, null);
|
||||
header('Location: /account/login');
|
||||
exit;
|
||||
}
|
||||
}
|
||||
if (isset($_SESSION['user_id'])) {
|
||||
header('Location: /account');
|
||||
}
|
||||
|
@ -131,12 +166,14 @@ class account
|
|||
]
|
||||
]));
|
||||
}
|
||||
|
||||
public static function logout()
|
||||
{
|
||||
session_unset();
|
||||
session_destroy();
|
||||
header('Location: /');
|
||||
}
|
||||
|
||||
public static function orders($defaults)
|
||||
{
|
||||
if (!isset($_SESSION['user_id'])) {
|
||||
|
@ -185,8 +222,8 @@ class account
|
|||
$ship_id = addresses::add(
|
||||
$ship['name'],
|
||||
$ship['company'],
|
||||
$ship['street'],
|
||||
$ship['boxapt'],
|
||||
$ship['addressLine1'],
|
||||
$ship['addressLine2'],
|
||||
$ship['city'],
|
||||
$ship['state'],
|
||||
$ship['zip'],
|
||||
|
@ -201,8 +238,8 @@ class account
|
|||
$_SESSION['success'] = "Shipping address saved!";
|
||||
header('Location: /account/shipping');
|
||||
}
|
||||
$email = $_SESSION['user_email'];
|
||||
$user = users::getByEmail($email);
|
||||
$user_id = $_SESSION['user_id'];
|
||||
$user = users::getById($user_id);
|
||||
$addresses = user_addresses::getShippingByUserId($user['id']);
|
||||
$default_shipping = null;
|
||||
$ship_addrs = [];
|
||||
|
@ -235,27 +272,30 @@ class account
|
|||
{
|
||||
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
|
||||
$email = $_POST['email'];
|
||||
if (empty($email)) {
|
||||
$_SESSION['error'] = 'Email is required.';
|
||||
}
|
||||
$existingUser = users::getByEmail($email);
|
||||
if ($existingUser) {
|
||||
$_SESSION['error'] = 'Email already exists. Please choose a different email or log in.';
|
||||
$_SESSION['last_post'] = $_POST;
|
||||
header('Location: /account/signup');
|
||||
exit;
|
||||
}
|
||||
if (empty($email)) {
|
||||
$_SESSION['error'] = 'Email is required.';
|
||||
}
|
||||
if (isset($_SESSION['error'])) {
|
||||
$useShipping = $_POST['use_shipping'] ?? false;
|
||||
$ship = addresses::validatePost("shipping");
|
||||
if (!isset($ship['name'])){
|
||||
$_SESSION['error'] = "Shipping address verification failed. Check your entry for errors.";
|
||||
$_SESSION['last_post'] = $_POST;
|
||||
header('Location: /account/signup');
|
||||
}
|
||||
$useShipping = $_POST['use_shipping'] ?? false;
|
||||
if ($useShipping) {
|
||||
$ship = addresses::validatePost("shipping");
|
||||
} else {
|
||||
$ship = addresses::validatePost("shipping");
|
||||
if (!$useShipping) {
|
||||
$bill = addresses::validatePost("billing");
|
||||
}
|
||||
if (empty($email)) {
|
||||
$_SESSION['error'] = 'Email is required.';
|
||||
if (!isset($bill['name'])){
|
||||
$_SESSION['error'] = "Billing address verification failed. Check your entry for errors.";
|
||||
$_SESSION['last_post'] = $_POST;
|
||||
header('Location: /account/signup');
|
||||
}
|
||||
}
|
||||
if (isset($_SESSION['error'])) {
|
||||
$_SESSION['last_post'] = $_POST;
|
||||
|
@ -264,8 +304,8 @@ class account
|
|||
$ship_id = addresses::add(
|
||||
$ship['name'],
|
||||
$ship['company'],
|
||||
$ship['street'],
|
||||
$ship['boxapt'],
|
||||
$ship['addressLine1'],
|
||||
$ship['addressLine2'],
|
||||
$ship['city'],
|
||||
$ship['state'],
|
||||
$ship['zip'],
|
||||
|
@ -278,8 +318,8 @@ class account
|
|||
$bill_id = addresses::add(
|
||||
$bill['name'],
|
||||
$bill['company'],
|
||||
$bill['street'],
|
||||
$bill['boxapt'],
|
||||
$bill['addressLine1'],
|
||||
$bill['addressLine2'],
|
||||
$bill['city'],
|
||||
$bill['state'],
|
||||
$bill['zip'],
|
||||
|
|
|
@ -8,60 +8,45 @@ class magic_link
|
|||
{
|
||||
public static function index()
|
||||
{
|
||||
$email = $_GET['email'] ?? null;
|
||||
$token = $_GET['token'] ?? null;
|
||||
$signup = $_GET['signup'] ?? null;
|
||||
|
||||
if (empty($email) && empty($token)) {
|
||||
$_SESSION['error'] = "Enter your email to get a login link";
|
||||
if (!$token) {
|
||||
$_SESSION['error'] = "Invalid or expired link.";
|
||||
header('Location: /account/login');
|
||||
exit;
|
||||
}
|
||||
|
||||
if ($email && empty($token) && empty($signup)) {
|
||||
$link = magic_links::add(email: $email);
|
||||
$subject = "Your Magic Sign-In Link";
|
||||
$message = "Copy and paste the link into your browser: $link";
|
||||
$HTML_message = "Click the link to sign in: <a href='$link'>$link</a>";
|
||||
app::send_mail(to: $email, from: $_ENV['SMTP_FROM'], from_name: $_ENV['APP_NAME'], subject: $subject, message: $message, HTML_message: $HTML_message);
|
||||
$_SESSION['success'] = 'Link sent to your email!';
|
||||
header('Location: /account/login');
|
||||
exit;
|
||||
}
|
||||
|
||||
if ($email && empty($token) && $signup == "1") {
|
||||
$link = magic_links::add(email: $email);
|
||||
$subject = "Your Magic Sign-In Link";
|
||||
$message = "Copy and paste the link into your browser: $link";
|
||||
$HTML_message = "Click the link to sign in: <a href='$link'>$link</a>";
|
||||
app::send_mail(to: $email, from: $_ENV['SMTP_FROM'], from_name: $_ENV['APP_NAME'], subject: $subject, message: $message, HTML_message: $HTML_message);
|
||||
$_SESSION['success'] = 'Account created! Please check your email inbox for the verification link.';
|
||||
header('Location: /account/login');
|
||||
exit;
|
||||
}
|
||||
|
||||
if ($token && empty($email)) {
|
||||
$link = magic_links::validate(token: $token);
|
||||
|
||||
} else {
|
||||
$link = magic_links::validateToken(token: $token);
|
||||
if (!$link) {
|
||||
$_SESSION['error'] = "Invalid or expired link.";
|
||||
header('Location: /account/login');
|
||||
exit;
|
||||
}
|
||||
// handle signup vs. login
|
||||
$user = users::getByEmail($link['email']);
|
||||
if ($user) {
|
||||
$user = $link['user_id'] ? users::getById($link['user_id']) : users::getByEmail($link['email']);
|
||||
if ($user) { // user with this email exists, log them in
|
||||
$_SESSION['user_email'] = $link['email'];
|
||||
$_SESSION['user_id'] = $user['id'];
|
||||
if (!$user['verified']) {
|
||||
users::verify($link['email']);
|
||||
}
|
||||
header('Location: /account');
|
||||
} else {
|
||||
// used to pre-fill email signup field
|
||||
$_SESSION['user_email'] = $link['email'];
|
||||
header('Location: /account/signup');
|
||||
exit;
|
||||
} else { // no users with this email
|
||||
$user_replacing_email = users::getByReplaceEmailToken($token);
|
||||
if ($user_replacing_email) { // user is replacing their email
|
||||
$user_id = $user_replacing_email['id'];
|
||||
users::updateEmailById($user_id, $link['email']);
|
||||
$_SESSION['user_email'] = $link['email'];
|
||||
$_SESSION['user_id'] = $user_id;
|
||||
if (!$user['verified']) {
|
||||
users::verify($link['email']);
|
||||
}
|
||||
header('Location: /account');
|
||||
exit;
|
||||
} else { // new user signup
|
||||
$_SESSION['user_email'] = $link['email'];
|
||||
header('Location: /account/signup');
|
||||
exit;
|
||||
}
|
||||
}
|
||||
exit();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,8 +10,8 @@ class addresses
|
|||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
name TEXT NOT NULL,
|
||||
company TEXT,
|
||||
street TEXT NOT NULL,
|
||||
boxapt TEXT NOT NULL,
|
||||
addressLine1 TEXT NOT NULL,
|
||||
addressLine2 TEXT NOT NULL,
|
||||
city TEXT NOT NULL,
|
||||
state TEXT NOT NULL,
|
||||
zip TEXT NOT NULL,
|
||||
|
@ -25,36 +25,52 @@ class addresses
|
|||
{
|
||||
$name = $_POST["{$type}_name"];
|
||||
$company = $_POST["{$type}_company"] ?? null;
|
||||
$boxapt = $_POST["{$type}_boxapt"] ?? null;
|
||||
$street = $_POST["{$type}_street"];
|
||||
$addressLine2 = $_POST["{$type}_addressLine2"] ?? null;
|
||||
$addressLine1 = $_POST["{$type}_addressLine1"];
|
||||
$city = $_POST["{$type}_city"];
|
||||
$state = $_POST["{$type}_state"];
|
||||
$zip = $_POST["{$type}_zip"];
|
||||
$phone = $_POST["{$type}_phone"];
|
||||
// check all required fields are set
|
||||
if (empty($name) || empty($street) || empty($city) || empty($state) || empty($zip) || empty($phone)) {
|
||||
if (empty($name) || empty($addressLine1) || empty($city) || empty($state) || empty($zip) || empty($phone)) {
|
||||
$_SESSION['error'] = "Missing required {$type} information.";
|
||||
}
|
||||
// TODO: find a match using postal database and return that
|
||||
$url = "https://nominatim.openstreetmap.org/search?" . http_build_query([
|
||||
"q" => implode(" ", array_filter([$addressLine1, $addressLine2, $city, $state, $zip])),
|
||||
"format" => "json",
|
||||
"addressdetails" => 1,
|
||||
"limit" => 1
|
||||
]);
|
||||
$ch = curl_init();
|
||||
curl_setopt($ch, CURLOPT_URL, $url);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
|
||||
curl_setopt($ch, CURLOPT_USERAGENT, "AddressValidator/1.0");
|
||||
$response = curl_exec($ch);
|
||||
curl_close($ch);
|
||||
$data = json_decode($response, true);
|
||||
if (empty($data)) {
|
||||
return ["error" => "Address not found"];
|
||||
}
|
||||
$addressDetails = $data[0]['address'];
|
||||
return [
|
||||
'name' => $name,
|
||||
'company' => $company,
|
||||
'street' => $street,
|
||||
'boxapt' => $boxapt,
|
||||
'city' => $city,
|
||||
'state' => $state,
|
||||
'zip' => $zip,
|
||||
'phone' => $phone
|
||||
'name' => $name,
|
||||
'company' => $company,
|
||||
"addressLine1" => ($addressDetails['house_number'] ?? '') . ' ' . ($addressDetails['building'] ?? '') . ' ' . ($addressDetails['road'] ?? ''),
|
||||
"addressLine2" => $addressLine2,
|
||||
"city" => $addressDetails['city'] ?? $addressDetails['town'] ?? $addressDetails['village'] ?? '',
|
||||
"state" => $addressDetails['state_code'] ?? ($addressDetails['state'] ?? ''),
|
||||
"zip" => $addressDetails['postcode'] ?? '',
|
||||
'phone' => $phone
|
||||
];
|
||||
}
|
||||
|
||||
public static function add($name, $company, $street, $boxapt, $city, $state, $zip, $phone, $billing, $shipping)
|
||||
public static function add($name, $company, $addressLine1, $addressLine2, $city, $state, $zip, $phone, $billing, $shipping)
|
||||
{
|
||||
$query = "INSERT INTO addresses (
|
||||
name,
|
||||
company,
|
||||
street,
|
||||
boxapt,
|
||||
addressLine1,
|
||||
addressLine2,
|
||||
city,
|
||||
state,
|
||||
zip,
|
||||
|
@ -64,8 +80,8 @@ class addresses
|
|||
) VALUES (
|
||||
:name,
|
||||
:company,
|
||||
:street,
|
||||
:boxapt,
|
||||
:addressLine1,
|
||||
:addressLine2,
|
||||
:city,
|
||||
:state,
|
||||
:zip,
|
||||
|
@ -76,8 +92,8 @@ class addresses
|
|||
$stmt = app::$db->prepare($query);
|
||||
$stmt->bindParam(':name', $name);
|
||||
$stmt->bindParam(':company', $company);
|
||||
$stmt->bindParam(':street', $street);
|
||||
$stmt->bindParam(':boxapt', $boxapt);
|
||||
$stmt->bindParam(':addressLine1', $addressLine1);
|
||||
$stmt->bindParam(':addressLine2', $addressLine2);
|
||||
$stmt->bindParam(':city', $city);
|
||||
$stmt->bindParam(':state', $state);
|
||||
$stmt->bindParam(':zip', $zip);
|
||||
|
|
53
src/models/cart_items.php
Normal file
53
src/models/cart_items.php
Normal file
|
@ -0,0 +1,53 @@
|
|||
<?php
|
||||
namespace app\models;
|
||||
|
||||
use app\app;
|
||||
|
||||
class cart_items
|
||||
{
|
||||
public static function init()
|
||||
{
|
||||
app::$db->exec("CREATE TABLE IF NOT EXISTS cart_items (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
cart_id INTEGER NOT NULL,
|
||||
product_id INTEGER NOT NULL,
|
||||
quantity INTEGER NOT NULL CHECK(quantity > 0),
|
||||
added_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (cart_id) REFERENCES carts(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (product_id) REFERENCES products(id)
|
||||
);");
|
||||
}
|
||||
|
||||
public static function addItem(int $cartId, int $productId, int $quantity)
|
||||
{
|
||||
$stmt = app::$db->prepare("INSERT INTO cart_items (cart_id, product_id, quantity)
|
||||
VALUES (:cart_id, :product_id, :quantity)");
|
||||
$stmt->execute([
|
||||
'cart_id' => $cartId,
|
||||
'product_id' => $productId,
|
||||
'quantity' => $quantity
|
||||
]);
|
||||
}
|
||||
|
||||
public static function updateItem(int $cartItemId, int $quantity)
|
||||
{
|
||||
$stmt = app::$db->prepare("UPDATE cart_items SET quantity = :quantity WHERE cart_item_id = :cart_item_id");
|
||||
$stmt->execute([
|
||||
'cart_item_id' => $cartItemId,
|
||||
'quantity' => $quantity
|
||||
]);
|
||||
}
|
||||
|
||||
public static function removeItem(int $cartItemId)
|
||||
{
|
||||
$stmt = app::$db->prepare("DELETE FROM cart_items WHERE cart_item_id = :cart_item_id");
|
||||
$stmt->execute(['cart_item_id' => $cartItemId]);
|
||||
}
|
||||
|
||||
public static function getCartItems(int $cartId): array
|
||||
{
|
||||
$stmt = app::$db->prepare("SELECT * FROM cart_items WHERE cart_id = :cart_id");
|
||||
$stmt->execute(['cart_id' => $cartId]);
|
||||
return $stmt->fetchAll();
|
||||
}
|
||||
}
|
|
@ -2,20 +2,42 @@
|
|||
namespace app\models;
|
||||
|
||||
use app\app;
|
||||
|
||||
class carts
|
||||
{
|
||||
public static function init()
|
||||
{
|
||||
app::$db->exec("CREATE TABLE IF NOT EXISTS order_items (
|
||||
order_item_id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
order_id INTEGER NOT NULL,
|
||||
product_id INTEGER NOT NULL,
|
||||
quantity INTEGER NOT NULL CHECK(quantity > 0),
|
||||
price REAL NOT NULL CHECK(price >= 0),
|
||||
FOREIGN KEY (order_id) REFERENCES orders(order_id),
|
||||
FOREIGN KEY (product_id) REFERENCES products(product_id)
|
||||
app::$db->exec("CREATE TABLE IF NOT EXISTS carts (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
user_id INTEGER NOT NULL,
|
||||
short_id TEXT UNIQUE NOT NULL,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (user_id) REFERENCES users(id)
|
||||
);");
|
||||
}
|
||||
|
||||
public static function addCart(int $userId): string
|
||||
{
|
||||
return self::createCart($userId, 'user_cart_id');
|
||||
}
|
||||
|
||||
public static function addSaved(int $userId): string
|
||||
{
|
||||
return self::createCart($userId, 'user_saved_for_later_id');
|
||||
}
|
||||
|
||||
private static function createCart(int $userId, string $column): string
|
||||
{
|
||||
$characters = '123456789ABCDEFGHJKLMNPQRSTUVWXYZ';
|
||||
$shortId = '';
|
||||
|
||||
for ($i = 0; $i < 6; $i++) {
|
||||
$shortId .= $characters[random_int(0, strlen($characters) - 1)];
|
||||
}
|
||||
|
||||
app::$db->prepare("INSERT INTO carts (user_id, short_id) VALUES (:user_id, :short_id)")
|
||||
->execute(['user_id' => $userId, 'short_id' => $shortId]);
|
||||
|
||||
return $shortId;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -8,38 +8,84 @@ class magic_links
|
|||
{
|
||||
app::$db->exec("CREATE TABLE IF NOT EXISTS magic_links (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
user_id INTEGER REFERENCES users(id),
|
||||
email TEXT NOT NULL,
|
||||
code TEXT NOT NULL,
|
||||
token TEXT NOT NULL,
|
||||
expires_at DATETIME NOT NULL,
|
||||
used BOOLEAN DEFAULT FALSE
|
||||
)");
|
||||
}
|
||||
|
||||
public static function add($email)
|
||||
public static function add($email, $user_id)
|
||||
{
|
||||
$code = str_pad(strval(random_int(0, 999999)), 6, "0", STR_PAD_LEFT);
|
||||
$token = bin2hex(random_bytes(32));
|
||||
$expires_at = date('Y-m-d H:i:s', time() + 60 * 15);
|
||||
app::$db->query("INSERT INTO magic_links (
|
||||
email,
|
||||
token,
|
||||
$query = "INSERT INTO magic_links (
|
||||
email,
|
||||
user_id,
|
||||
token,
|
||||
code,
|
||||
expires_at
|
||||
) VALUES (
|
||||
'$email',
|
||||
'$token',
|
||||
'$expires_at'
|
||||
)");
|
||||
return $_ENV['APP_HOST'] . "/magic-link?token=" . urlencode($token);
|
||||
:email,
|
||||
:user_id,
|
||||
:token,
|
||||
:code,
|
||||
:expires_at
|
||||
)";
|
||||
$stmt = app::$db->prepare($query);
|
||||
$stmt->bindParam(':email', $email);
|
||||
$stmt->bindParam(':user_id', $user_id);
|
||||
$stmt->bindParam(':token', $token);
|
||||
$stmt->bindParam(':code', $code);
|
||||
$stmt->bindParam(':expires_at', $expires_at);
|
||||
$stmt->execute();
|
||||
$link = $_ENV['APP_HOST'] . "/magic-link?token=" . urlencode($token);
|
||||
$subject = "Your Magic Sign-In Link";
|
||||
$message = "Enter this code into the sign-in form\n$code\n or copy-paste this link into your browser to sign in:\n$link";
|
||||
$HTML_message = "Click the link to sign in: <a href='$link'>$link</a> or enter this code:<br/>$code";
|
||||
app::send_mail(to: $email, from: $_ENV['SMTP_FROM'], from_name: $_ENV['APP_NAME'], subject: $subject, message: $message, HTML_message: $HTML_message);
|
||||
$_SESSION['success'] = 'Link sent to your email!';
|
||||
return $token;
|
||||
}
|
||||
|
||||
public static function validate($token)
|
||||
public static function validateToken($token)
|
||||
{
|
||||
$link = app::$db->query("SELECT * FROM magic_links
|
||||
WHERE token = '$token'
|
||||
AND used = FALSE
|
||||
AND expires_at > datetime('now')
|
||||
")->fetch(\PDO::FETCH_ASSOC);
|
||||
// void the token once validated
|
||||
app::$db->query("UPDATE magic_links SET used = TRUE WHERE token = '$token'");
|
||||
$query = "SELECT * FROM magic_links
|
||||
WHERE token = :token
|
||||
AND used = FALSE
|
||||
AND expires_at > datetime('now')";
|
||||
$stmt = app::$db->prepare($query);
|
||||
$stmt->bindParam(':token', $token);
|
||||
$stmt->execute();
|
||||
$link = $stmt->fetch(\PDO::FETCH_ASSOC);
|
||||
|
||||
$updateQuery = "UPDATE magic_links SET used = TRUE WHERE token = :token";
|
||||
$updateStmt = app::$db->prepare($updateQuery);
|
||||
$updateStmt->bindParam(':token', $token);
|
||||
$updateStmt->execute();
|
||||
|
||||
return $link;
|
||||
}
|
||||
|
||||
public static function validateCode($code)
|
||||
{
|
||||
$query = "SELECT * FROM magic_links
|
||||
WHERE code = :code
|
||||
AND used = FALSE
|
||||
AND expires_at > datetime('now')";
|
||||
$stmt = app::$db->prepare($query);
|
||||
$stmt->bindParam(':code', $code);
|
||||
$stmt->execute();
|
||||
$link = $stmt->fetch(\PDO::FETCH_ASSOC);
|
||||
|
||||
$updateQuery = "UPDATE magic_links SET used = TRUE WHERE code = :code";
|
||||
$updateStmt = app::$db->prepare($updateQuery);
|
||||
$updateStmt->bindParam(':code', $code);
|
||||
$updateStmt->execute();
|
||||
|
||||
return $link;
|
||||
}
|
||||
}
|
||||
|
|
61
src/models/order_items.php
Normal file
61
src/models/order_items.php
Normal file
|
@ -0,0 +1,61 @@
|
|||
<?php
|
||||
namespace app\models;
|
||||
|
||||
use app\app;
|
||||
|
||||
class order_items
|
||||
{
|
||||
public static function init()
|
||||
{
|
||||
app::$db->exec("CREATE TABLE IF NOT EXISTS order_items (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
order_id INTEGER NOT NULL,
|
||||
product_id INTEGER NOT NULL,
|
||||
quantity INTEGER NOT NULL CHECK(quantity > 0),
|
||||
added_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (order_id) REFERENCES orders(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (product_id) REFERENCES products(id)
|
||||
);");
|
||||
}
|
||||
|
||||
public static function addItem(int $orderId, int $productId, int $quantity)
|
||||
{
|
||||
if ($quantity <= 0) {
|
||||
throw new \InvalidArgumentException('Quantity must be greater than zero.');
|
||||
}
|
||||
|
||||
$stmt = app::$db->prepare("INSERT INTO order_items (order_id, product_id, quantity)
|
||||
VALUES (:order_id, :product_id, :quantity)");
|
||||
$stmt->execute([
|
||||
'order_id' => $orderId,
|
||||
'product_id' => $productId,
|
||||
'quantity' => $quantity
|
||||
]);
|
||||
}
|
||||
|
||||
public static function updateItem(int $orderItemId, int $quantity)
|
||||
{
|
||||
if ($quantity <= 0) {
|
||||
throw new \InvalidArgumentException('Quantity must be greater than zero.');
|
||||
}
|
||||
|
||||
$stmt = app::$db->prepare("UPDATE order_items SET quantity = :quantity WHERE order_item_id = :order_item_id");
|
||||
$stmt->execute([
|
||||
'order_item_id' => $orderItemId,
|
||||
'quantity' => $quantity
|
||||
]);
|
||||
}
|
||||
|
||||
public static function removeItem(int $orderItemId)
|
||||
{
|
||||
$stmt = app::$db->prepare("DELETE FROM order_items WHERE order_item_id = :order_item_id");
|
||||
$stmt->execute(['order_item_id' => $orderItemId]);
|
||||
}
|
||||
|
||||
public static function getOrderItems(int $orderId): array
|
||||
{
|
||||
$stmt = app::$db->prepare("SELECT * FROM order_items WHERE order_id = :order_id");
|
||||
$stmt->execute(['order_id' => $orderId]);
|
||||
return $stmt->fetchAll();
|
||||
}
|
||||
}
|
|
@ -2,17 +2,72 @@
|
|||
namespace app\models;
|
||||
|
||||
use app\app;
|
||||
|
||||
class orders
|
||||
{
|
||||
const STATUSES = [
|
||||
'SHIPPED', 'PENDING', 'HOLD', 'PARTIAL',
|
||||
'BACKORDER', 'FAILED', 'CANCELED', 'PROCESSING'
|
||||
];
|
||||
|
||||
public static function init()
|
||||
{
|
||||
app::$db->exec("CREATE TABLE IF NOT EXISTS orders (
|
||||
order_id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
user_id INTEGER NOT NULL,
|
||||
total_amount REAL NOT NULL CHECK(total_amount >= 0),
|
||||
status TEXT NOT NULL CHECK(status IN ('pending', 'completed', 'cancelled')),
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (user_id) REFERENCES users(user_id)
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
user_id INTEGER NOT NULL,
|
||||
value_sats INTEGER NOT NULL CHECK(value_sats >= 0),
|
||||
value_cents INTEGER NOT NULL CHECK(value_cents >= 0),
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
status TEXT CHECK(status IN ('" . implode("', '", self::STATUSES) . "')) NOT NULL DEFAULT 'PENDING',
|
||||
FOREIGN KEY (user_id) REFERENCES users(id)
|
||||
);");
|
||||
}
|
||||
|
||||
public static function createOrder(int $userId, int $valueSats, int $valueCents, string $status = 'PENDING'): int
|
||||
{
|
||||
self::validateStatus($status);
|
||||
|
||||
$stmt = app::$db->prepare("INSERT INTO orders (user_id, value_sats, value_cents, status)
|
||||
VALUES (:user_id, :value_sats, :value_cents, :status)");
|
||||
$stmt->execute([
|
||||
'user_id' => $userId,
|
||||
'value_sats' => $valueSats,
|
||||
'value_cents' => $valueCents,
|
||||
'status' => $status
|
||||
]);
|
||||
|
||||
return app::$db->lastInsertId();
|
||||
}
|
||||
|
||||
public static function updateStatus(int $orderId, string $status)
|
||||
{
|
||||
self::validateStatus($status);
|
||||
|
||||
$stmt = app::$db->prepare("UPDATE orders SET status = :status WHERE order_id = :order_id");
|
||||
$stmt->execute([
|
||||
'order_id' => $orderId,
|
||||
'status' => $status
|
||||
]);
|
||||
}
|
||||
|
||||
public static function getOrder(int $orderId): array
|
||||
{
|
||||
$stmt = app::$db->prepare("SELECT * FROM orders WHERE order_id = :order_id");
|
||||
$stmt->execute(['order_id' => $orderId]);
|
||||
return $stmt->fetch() ?: [];
|
||||
}
|
||||
|
||||
public static function getUserOrders(int $userId): array
|
||||
{
|
||||
$stmt = app::$db->prepare("SELECT * FROM orders WHERE user_id = :user_id ORDER BY created_at DESC");
|
||||
$stmt->execute(['user_id' => $userId]);
|
||||
return $stmt->fetchAll();
|
||||
}
|
||||
|
||||
private static function validateStatus(string $status)
|
||||
{
|
||||
if (!in_array($status, self::STATUSES, true)) {
|
||||
throw new \InvalidArgumentException("Invalid order status: $status");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,16 +2,69 @@
|
|||
namespace app\models;
|
||||
|
||||
use app\app;
|
||||
|
||||
class products
|
||||
{
|
||||
public static function init()
|
||||
{
|
||||
app::$db->exec("CREATE TABLE IF NOT EXISTS products (
|
||||
product_id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
name TEXT NOT NULL,
|
||||
description TEXT,
|
||||
price REAL NOT NULL CHECK(price >= 0),
|
||||
qty INTEGER NOT NULL DEFAULT 0 CHECK(qty >= 0)
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
title TEXT NOT NULL,
|
||||
desc TEXT,
|
||||
stock_qty INTEGER NOT NULL DEFAULT 0 CHECK(stock_qty >= 0),
|
||||
specs_json TEXT,
|
||||
sats_price INTEGER NOT NULL DEFAULT 0 CHECK(sats_price >= 0),
|
||||
cents_price INTEGER NOT NULL DEFAULT 0 CHECK(cents_price >= 0),
|
||||
digital BOOLEAN NOT NULL DEFAULT 0,
|
||||
subscription BOOLEAN NOT NULL DEFAULT 0,
|
||||
image_url_0 TEXT,
|
||||
image_url_1 TEXT,
|
||||
image_url_2 TEXT,
|
||||
image_url_3 TEXT,
|
||||
image_url_4 TEXT,
|
||||
image_url_5 TEXT,
|
||||
image_url_6 TEXT,
|
||||
image_url_7 TEXT,
|
||||
image_url_8 TEXT,
|
||||
image_url_9 TEXT,
|
||||
image_url_10 TEXT,
|
||||
image_url_11 TEXT
|
||||
)");
|
||||
}
|
||||
|
||||
public static function add($title, $desc, $stock_qty, $specs_json, $sats_price, $cents_price, $digital, $subscription, $images)
|
||||
{
|
||||
$stmt = app::$db->prepare("INSERT INTO products (
|
||||
title, desc, stock_qty, specs_json, sats_price, cents_price, digital, subscription,
|
||||
image_url_0, image_url_1, image_url_2, image_url_3, image_url_4, image_url_5,
|
||||
image_url_6, image_url_7, image_url_8, image_url_9, image_url_10, image_url_11
|
||||
) VALUES (
|
||||
:title, :desc, :stock_qty, :specs_json, :sats_price, :cents_price, :digital, :subscription,
|
||||
:image_url_0, :image_url_1, :image_url_2, :image_url_3, :image_url_4, :image_url_5,
|
||||
:image_url_6, :image_url_7, :image_url_8, :image_url_9, :image_url_10, :image_url_11
|
||||
)");
|
||||
|
||||
$stmt->execute([
|
||||
':title' => $title,
|
||||
':desc' => $desc,
|
||||
':stock_qty' => $stock_qty,
|
||||
':specs_json' => $specs_json,
|
||||
':sats_price' => $sats_price,
|
||||
':cents_price' => $cents_price,
|
||||
':digital' => (int) $digital,
|
||||
':subscription' => (int) $subscription,
|
||||
':image_url_0' => $images[0] ?? null,
|
||||
':image_url_1' => $images[1] ?? null,
|
||||
':image_url_2' => $images[2] ?? null,
|
||||
':image_url_3' => $images[3] ?? null,
|
||||
':image_url_4' => $images[4] ?? null,
|
||||
':image_url_5' => $images[5] ?? null,
|
||||
':image_url_6' => $images[6] ?? null,
|
||||
':image_url_7' => $images[7] ?? null,
|
||||
':image_url_8' => $images[8] ?? null,
|
||||
':image_url_9' => $images[9] ?? null,
|
||||
':image_url_10' => $images[10] ?? null,
|
||||
':image_url_11' => $images[11] ?? null
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
70
src/models/quote_items.php
Normal file
70
src/models/quote_items.php
Normal file
|
@ -0,0 +1,70 @@
|
|||
<?php
|
||||
namespace app\models;
|
||||
|
||||
use app\app;
|
||||
|
||||
class quote_items
|
||||
{
|
||||
public static function init()
|
||||
{
|
||||
app::$db->exec("CREATE TABLE IF NOT EXISTS quote_items (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
quote_id INTEGER NOT NULL,
|
||||
product_id INTEGER NOT NULL,
|
||||
quantity INTEGER NOT NULL CHECK(quantity > 0),
|
||||
price REAL NOT NULL CHECK(price >= 0),
|
||||
added_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (quote_id) REFERENCES quotes(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (product_id) REFERENCES products(id)
|
||||
);");
|
||||
}
|
||||
|
||||
public static function addItem(int $quoteId, int $productId, int $quantity, float $price)
|
||||
{
|
||||
if ($quantity <= 0) {
|
||||
throw new \InvalidArgumentException('Quantity must be greater than zero.');
|
||||
}
|
||||
if ($price < 0) {
|
||||
throw new \InvalidArgumentException('Price must be non-negative.');
|
||||
}
|
||||
|
||||
$stmt = app::$db->prepare("INSERT INTO quote_items (quote_id, product_id, quantity, price)
|
||||
VALUES (:quote_id, :product_id, :quantity, :price)");
|
||||
$stmt->execute([
|
||||
'quote_id' => $quoteId,
|
||||
'product_id' => $productId,
|
||||
'quantity' => $quantity,
|
||||
'price' => $price
|
||||
]);
|
||||
}
|
||||
|
||||
public static function updateItem(int $quoteItemId, int $quantity, float $price)
|
||||
{
|
||||
if ($quantity <= 0) {
|
||||
throw new \InvalidArgumentException('Quantity must be greater than zero.');
|
||||
}
|
||||
if ($price < 0) {
|
||||
throw new \InvalidArgumentException('Price must be non-negative.');
|
||||
}
|
||||
|
||||
$stmt = app::$db->prepare("UPDATE quote_items SET quantity = :quantity, price = :price WHERE quote_item_id = :quote_item_id");
|
||||
$stmt->execute([
|
||||
'quote_item_id' => $quoteItemId,
|
||||
'quantity' => $quantity,
|
||||
'price' => $price
|
||||
]);
|
||||
}
|
||||
|
||||
public static function removeItem(int $quoteItemId)
|
||||
{
|
||||
$stmt = app::$db->prepare("DELETE FROM quote_items WHERE quote_item_id = :quote_item_id");
|
||||
$stmt->execute(['quote_item_id' => $quoteItemId]);
|
||||
}
|
||||
|
||||
public static function getQuoteItems(int $quoteId): array
|
||||
{
|
||||
$stmt = app::$db->prepare("SELECT * FROM quote_items WHERE quote_id = :quote_id");
|
||||
$stmt->execute(['quote_id' => $quoteId]);
|
||||
return $stmt->fetchAll();
|
||||
}
|
||||
}
|
69
src/models/quotes.php
Normal file
69
src/models/quotes.php
Normal file
|
@ -0,0 +1,69 @@
|
|||
<?php
|
||||
namespace app\models;
|
||||
|
||||
use app\app;
|
||||
|
||||
class quotes
|
||||
{
|
||||
private const STATUSES = [
|
||||
'DRAFT', 'PUBLISHED', 'SENT', 'PURCHASED',
|
||||
'EXPIRED', 'CANCELED'
|
||||
];
|
||||
|
||||
public static function init()
|
||||
{
|
||||
app::$db->exec("CREATE TABLE IF NOT EXISTS quotes (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
user_id INTEGER NOT NULL,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
status TEXT CHECK(status IN ('" . implode("', '", self::STATUSES) . "')) NOT NULL DEFAULT 'DRAFT',
|
||||
FOREIGN KEY (user_id) REFERENCES users(id)
|
||||
);");
|
||||
}
|
||||
|
||||
public static function createQuote(int $userId, string $status = 'DRAFT'): int
|
||||
{
|
||||
self::validateStatus($status);
|
||||
|
||||
$stmt = app::$db->prepare("INSERT INTO quotes (user_id, status)
|
||||
VALUES (:user_id, :status)");
|
||||
$stmt->execute([
|
||||
'user_id' => $userId,
|
||||
'status' => $status
|
||||
]);
|
||||
|
||||
return app::$db->lastInsertId();
|
||||
}
|
||||
|
||||
public static function updateStatus(int $quoteId, string $status)
|
||||
{
|
||||
self::validateStatus($status);
|
||||
|
||||
$stmt = app::$db->prepare("UPDATE quotes SET status = :status WHERE quote_id = :quote_id");
|
||||
$stmt->execute([
|
||||
'quote_id' => $quoteId,
|
||||
'status' => $status
|
||||
]);
|
||||
}
|
||||
|
||||
public static function getQuote(int $quoteId): array
|
||||
{
|
||||
$stmt = app::$db->prepare("SELECT * FROM quotes WHERE quote_id = :quote_id");
|
||||
$stmt->execute(['quote_id' => $quoteId]);
|
||||
return $stmt->fetch() ?: [];
|
||||
}
|
||||
|
||||
public static function getUserQuotes(int $userId): array
|
||||
{
|
||||
$stmt = app::$db->prepare("SELECT * FROM quotes WHERE user_id = :user_id ORDER BY created_at DESC");
|
||||
$stmt->execute(['user_id' => $userId]);
|
||||
return $stmt->fetchAll();
|
||||
}
|
||||
|
||||
private static function validateStatus(string $status)
|
||||
{
|
||||
if (!in_array($status, self::STATUSES, true)) {
|
||||
throw new \InvalidArgumentException("Invalid quote status: $status");
|
||||
}
|
||||
}
|
||||
}
|
101
src/models/subscriptions.php
Normal file
101
src/models/subscriptions.php
Normal file
|
@ -0,0 +1,101 @@
|
|||
<?php
|
||||
namespace app\models;
|
||||
|
||||
use app\app;
|
||||
|
||||
class Subscriptions
|
||||
{
|
||||
const STATES = [
|
||||
'TRIAL', 'START', 'RENEWAL'
|
||||
];
|
||||
|
||||
const STATUS = [
|
||||
'COMPLETED', 'CANCELED'
|
||||
];
|
||||
|
||||
public static function init()
|
||||
{
|
||||
app::$db->exec("CREATE TABLE IF NOT EXISTS subscriptions (
|
||||
subscription_id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
user_id INTEGER NOT NULL,
|
||||
product_id INTEGER NOT NULL,
|
||||
start_date DATETIME NOT NULL,
|
||||
renews_at DATETIME NOT NULL,
|
||||
status TEXT CHECK(status IN ('" . implode("', '", self::STATUS) . "')) NOT NULL DEFAULT 'COMPLETED',
|
||||
state TEXT CHECK(state IN ('" . implode("', '", self::STATES) . "')) NOT NULL DEFAULT 'TRIAL',
|
||||
invoice_date DATETIME NOT NULL,
|
||||
FOREIGN KEY (user_id) REFERENCES users(id),
|
||||
FOREIGN KEY (product_id) REFERENCES products(id)
|
||||
);");
|
||||
}
|
||||
|
||||
public static function createSubscription(int $userId, int $productId, string $state = 'TRIAL', string $status = 'COMPLETED', string $startDate, string $renewAt, string $invoiceDate): int
|
||||
{
|
||||
self::validateState($state);
|
||||
self::validateStatus($status);
|
||||
|
||||
$stmt = app::$db->prepare("INSERT INTO subscriptions (user_id, product_id, state, status, start_date, renews_at, invoice_date)
|
||||
VALUES (:user_id, :product_id, :state, :status, :start_date, :renews_at, :invoice_date)");
|
||||
$stmt->execute([
|
||||
'user_id' => $userId,
|
||||
'product_id' => $productId,
|
||||
'state' => $state,
|
||||
'status' => $status,
|
||||
'start_date' => $startDate,
|
||||
'renews_at' => $renewAt,
|
||||
'invoice_date' => $invoiceDate
|
||||
]);
|
||||
|
||||
return app::$db->lastInsertId();
|
||||
}
|
||||
|
||||
public static function updateState(int $subscriptionId, string $state)
|
||||
{
|
||||
self::validateState($state);
|
||||
|
||||
$stmt = app::$db->prepare("UPDATE subscriptions SET state = :state WHERE subscription_id = :subscription_id");
|
||||
$stmt->execute([
|
||||
'subscription_id' => $subscriptionId,
|
||||
'state' => $state
|
||||
]);
|
||||
}
|
||||
|
||||
public static function updateStatus(int $subscriptionId, string $status)
|
||||
{
|
||||
self::validateStatus($status);
|
||||
|
||||
$stmt = app::$db->prepare("UPDATE subscriptions SET status = :status WHERE subscription_id = :subscription_id");
|
||||
$stmt->execute([
|
||||
'subscription_id' => $subscriptionId,
|
||||
'status' => $status
|
||||
]);
|
||||
}
|
||||
|
||||
public static function getSubscription(int $subscriptionId): array
|
||||
{
|
||||
$stmt = app::$db->prepare("SELECT * FROM subscriptions WHERE subscription_id = :subscription_id");
|
||||
$stmt->execute(['subscription_id' => $subscriptionId]);
|
||||
return $stmt->fetch() ?: [];
|
||||
}
|
||||
|
||||
public static function getUserSubscriptions(int $userId): array
|
||||
{
|
||||
$stmt = app::$db->prepare("SELECT * FROM subscriptions WHERE user_id = :user_id ORDER BY start_date DESC");
|
||||
$stmt->execute(['user_id' => $userId]);
|
||||
return $stmt->fetchAll();
|
||||
}
|
||||
|
||||
private static function validateState(string $state)
|
||||
{
|
||||
if (!in_array($state, self::STATES, true)) {
|
||||
throw new \InvalidArgumentException("Invalid subscription state: $state");
|
||||
}
|
||||
}
|
||||
|
||||
private static function validateStatus(string $status)
|
||||
{
|
||||
if (!in_array($status, self::STATUS, true)) {
|
||||
throw new \InvalidArgumentException("Invalid subscription status: $status");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,53 +2,89 @@
|
|||
namespace app\models;
|
||||
|
||||
use app\app;
|
||||
|
||||
class transactions
|
||||
{
|
||||
const TYPES = ['CREDIT', 'REWARD', 'REDEEM', 'REVOKE', 'DEPOSIT'];
|
||||
|
||||
public static function init()
|
||||
{
|
||||
app::$db->exec("CREATE TABLE transactions (
|
||||
$typesList = "'" . implode("', '", self::TYPES) . "'";
|
||||
app::$db->exec("CREATE TABLE IF NOT EXISTS transactions (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
user_id INTEGER NOT NULL,
|
||||
type TEXT CHECK(transaction_type IN ('credit', 'spend', 'withdraw')) NOT NULL,
|
||||
cents REAL DEFAULT 0,
|
||||
sats REAL DEFAULT 0,
|
||||
type TEXT CHECK(type IN ($typesList)) NOT NULL,
|
||||
cents INTEGER DEFAULT 0,
|
||||
sats INTEGER DEFAULT 0,
|
||||
date TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (user_id) REFERENCES users(id)
|
||||
)");
|
||||
}
|
||||
|
||||
public static function add($user_id, $transaction_type, $cents, $sats_amount)
|
||||
public static function add($user_id, $transaction_type, $cents, $sats)
|
||||
{
|
||||
$query = "INSERT INTO transactions (
|
||||
user_id,
|
||||
type,
|
||||
cents,
|
||||
sats
|
||||
) VALUES (
|
||||
:user_id,
|
||||
:transaction_type,
|
||||
:cents,
|
||||
:sats
|
||||
)";
|
||||
if (!in_array($transaction_type, self::TYPES)) {
|
||||
throw new \Exception("Invalid transaction type.");
|
||||
}
|
||||
if ($cents < 0 || $sats < 0) {
|
||||
throw new \Exception("Amounts must be non-negative integers.");
|
||||
}
|
||||
|
||||
$currentBalance = self::getUserBalance($user_id);
|
||||
|
||||
if (in_array($transaction_type, ['REDEEM', 'REVOKE']) && ($currentBalance['total_cents'] < $cents || $currentBalance['total_sats'] < $sats)) {
|
||||
throw new \Exception("Insufficient funds.");
|
||||
}
|
||||
|
||||
$query = "INSERT INTO transactions (user_id, type, cents, sats) VALUES (:user_id, :transaction_type, :cents, :sats)";
|
||||
$stmt = app::$db->prepare($query);
|
||||
$stmt->bindParam(':user_id', $user_id);
|
||||
$stmt->bindParam(':transaction_type', $transaction_type);
|
||||
$stmt->bindParam(':cents', $cents);
|
||||
$stmt->bindParam(':sats', $sats_amount);
|
||||
$stmt->bindParam(':sats', $sats);
|
||||
$stmt->execute();
|
||||
|
||||
return app::$db->lastInsertId();
|
||||
}
|
||||
|
||||
public static function getUserBalance($user_id)
|
||||
{
|
||||
$query = "SELECT SUM(cents) AS total_cents,
|
||||
SUM(sats) AS total_sats
|
||||
FROM transactions
|
||||
WHERE user_id = :user_id";
|
||||
$query = "SELECT COALESCE(SUM(cents), 0) AS total_cents, COALESCE(SUM(sats), 0) AS total_sats FROM transactions WHERE user_id = :user_id";
|
||||
$stmt = app::$db->prepare($query);
|
||||
$stmt->bindParam(':user_id', $user_id);
|
||||
$stmt->execute();
|
||||
$result = $stmt->fetch();
|
||||
return $result;
|
||||
return $stmt->fetch();
|
||||
}
|
||||
|
||||
public static function getRecent($n)
|
||||
{
|
||||
$query = "SELECT * FROM transactions ORDER BY date DESC LIMIT :n";
|
||||
$stmt = app::$db->prepare($query);
|
||||
$stmt->bindParam(':n', $n, \PDO::PARAM_INT);
|
||||
$stmt->execute();
|
||||
return $stmt->fetchAll();
|
||||
}
|
||||
|
||||
public static function getWhales($n, $currency)
|
||||
{
|
||||
if (!in_array($currency, ['cents', 'sats'])) {
|
||||
throw new \Exception("Invalid currency type.");
|
||||
}
|
||||
$query = "SELECT user_id, COALESCE(SUM($currency), 0) AS total FROM transactions GROUP BY user_id ORDER BY total DESC LIMIT :n";
|
||||
$stmt = app::$db->prepare($query);
|
||||
$stmt->bindParam(':n', $n, \PDO::PARAM_INT);
|
||||
$stmt->execute();
|
||||
return $stmt->fetchAll();
|
||||
}
|
||||
|
||||
public static function liabilities($currency)
|
||||
{
|
||||
if (!in_array($currency, ['cents', 'sats'])) {
|
||||
throw new \Exception("Invalid currency type.");
|
||||
}
|
||||
$query = "SELECT COALESCE(SUM($currency), 0) AS total FROM transactions";
|
||||
$stmt = app::$db->prepare($query);
|
||||
$stmt->execute();
|
||||
return $stmt->fetchColumn();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,19 +18,27 @@ class user_addresses
|
|||
|
||||
public static function getShippingByUserId($id)
|
||||
{
|
||||
$addrs = app::$db->query("SELECT a.* FROM users u
|
||||
$query = "SELECT a.* FROM users u
|
||||
JOIN user_addresses ua ON u.id = ua.user_id
|
||||
JOIN addresses a ON ua.address_id = a.id
|
||||
WHERE u.id = '$id' AND a.shipping = 1")->fetch(\PDO::FETCH_ASSOC);
|
||||
WHERE u.id = :id AND a.shipping = 1";
|
||||
$stmt = app::$db->prepare($query);
|
||||
$stmt->bindParam(':id', $id);
|
||||
$stmt->execute();
|
||||
$addrs = $stmt->fetch(\PDO::FETCH_ASSOC);
|
||||
return [$addrs];
|
||||
}
|
||||
|
||||
public static function getBillingByUserId($id)
|
||||
{
|
||||
$addrs = app::$db->query("SELECT a.* FROM users u
|
||||
$query = "SELECT a.* FROM users u
|
||||
JOIN user_addresses ua ON u.id = ua.user_id
|
||||
JOIN addresses a ON ua.address_id = a.id
|
||||
WHERE u.id = '$id' AND a.billing = 1")->fetch(\PDO::FETCH_ASSOC);
|
||||
WHERE u.id = :id AND a.billing = 1";
|
||||
$stmt = app::$db->prepare($query);
|
||||
$stmt->bindParam(':id', $id);
|
||||
$stmt->execute();
|
||||
$addrs = $stmt->fetch(\PDO::FETCH_ASSOC);
|
||||
return [$addrs];
|
||||
}
|
||||
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
namespace app\models;
|
||||
|
||||
use app\app;
|
||||
use swentel\nostr\Key\Key;
|
||||
|
||||
class users
|
||||
{
|
||||
public static function init()
|
||||
|
@ -14,15 +16,44 @@ class users
|
|||
opt_in_promotional BOOLEAN NOT NULL,
|
||||
verified BOOLEAN NOT NULL,
|
||||
dark_theme BOOLEAN NOT NULL,
|
||||
generated_base58 TEXT UNIQUE,
|
||||
nsec TEXT,
|
||||
npub TEXT NOT NULL,
|
||||
attached_lightning_address TEXT,
|
||||
replace_email_token TEXT,
|
||||
name TEXT,
|
||||
company_name TEXT,
|
||||
company_type TEXT,
|
||||
company_size TEXT,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
||||
)");
|
||||
app::$db->exec('CREATE INDEX IF NOT EXISTS idx_user_email ON users (email)');
|
||||
}
|
||||
|
||||
public static function updateReplaceEmailTokenById($user_id, $replace_token)
|
||||
{
|
||||
$query = "UPDATE users SET replace_email_token = :replace_token WHERE id = :user_id";
|
||||
$stmt = app::$db->prepare($query);
|
||||
$stmt->bindParam(':replace_token', $replace_token);
|
||||
$stmt->bindParam(':user_id', $user_id);
|
||||
$stmt->execute();
|
||||
}
|
||||
|
||||
public static function updateEmailById($user_id, $email)
|
||||
{
|
||||
$query = "UPDATE users SET email = :email WHERE id = :user_id";
|
||||
$stmt = app::$db->prepare($query);
|
||||
$stmt->bindParam(':email', $email);
|
||||
$stmt->bindParam(':user_id', $user_id);
|
||||
$stmt->execute();
|
||||
users::updateReplaceEmailTokenById($user_id, null);
|
||||
}
|
||||
|
||||
public static function getByReplaceEmailToken($token)
|
||||
{
|
||||
$query = "SELECT * FROM users WHERE replace_email_token = :token";
|
||||
$stmt = app::$db->prepare($query);
|
||||
$stmt->bindParam(':token', $token);
|
||||
$stmt->execute();
|
||||
return $stmt->fetch(\PDO::FETCH_ASSOC);
|
||||
}
|
||||
|
||||
public static function updateProfileById($user_id, $post)
|
||||
|
@ -44,20 +75,29 @@ class users
|
|||
|
||||
public static function add($email, $ship_id, $bill_id, $opt_in_promotional, $verified, $dark_theme)
|
||||
{
|
||||
$key = new Key();
|
||||
$private_key = $key->generatePrivateKey();
|
||||
$public_key = $key->getPublicKey($private_key);
|
||||
$npub = $key->convertPublicKeyToBech32($public_key);
|
||||
$nsec = $key->convertPrivateKeyToBech32($private_key);
|
||||
$query = "INSERT INTO users (
|
||||
email,
|
||||
shipping_address_id,
|
||||
billing_address_id,
|
||||
opt_in_promotional,
|
||||
verified,
|
||||
dark_theme
|
||||
dark_theme,
|
||||
nsec,
|
||||
npub
|
||||
) VALUES (
|
||||
:email,
|
||||
:shipping_address_id,
|
||||
:billing_address_id,
|
||||
:opt_in_promotional,
|
||||
:verified,
|
||||
:dark_theme
|
||||
:dark_theme,
|
||||
:nsec,
|
||||
:npub
|
||||
)";
|
||||
$stmt = app::$db->prepare($query);
|
||||
$stmt->bindParam(':email', $email);
|
||||
|
@ -66,18 +106,35 @@ class users
|
|||
$stmt->bindParam(':opt_in_promotional', $opt_in_promotional);
|
||||
$stmt->bindParam(':verified', $verified);
|
||||
$stmt->bindParam(':dark_theme', $dark_theme);
|
||||
$stmt->bindParam(':nsec', $nsec);
|
||||
$stmt->bindParam(':npub', $npub);
|
||||
$stmt->execute();
|
||||
return app::$db->lastInsertId();
|
||||
|
||||
}
|
||||
|
||||
public static function verify($email)
|
||||
{
|
||||
app::$db->exec("UPDATE users SET verified = 1 WHERE email = '$email'");
|
||||
$query = "UPDATE users SET verified = 1 WHERE email = :email";
|
||||
$stmt = app::$db->prepare($query);
|
||||
$stmt->bindParam(':email', $email);
|
||||
$stmt->execute();
|
||||
}
|
||||
|
||||
public static function getById($id)
|
||||
{
|
||||
$query = "SELECT * FROM users WHERE id = :id";
|
||||
$stmt = app::$db->prepare($query);
|
||||
$stmt->bindParam(':id', $id);
|
||||
$stmt->execute();
|
||||
return $stmt->fetch(\PDO::FETCH_ASSOC);
|
||||
}
|
||||
|
||||
public static function getByEmail($email)
|
||||
{
|
||||
return app::$db->query("SELECT * FROM users WHERE email = '$email'")->fetch(\PDO::FETCH_ASSOC);
|
||||
$query = "SELECT * FROM users WHERE email = :email";
|
||||
$stmt = app::$db->prepare($query);
|
||||
$stmt->bindParam(':email', $email);
|
||||
$stmt->execute();
|
||||
return $stmt->fetch(\PDO::FETCH_ASSOC);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,32 +1,3 @@
|
|||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
/* #Mega Menu Styles
|
||||
–––––––––––––––––––––––––––––––––––––––––––––––––– */
|
||||
.mega-menu {
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
z-index: -900;
|
||||
left: 0;
|
||||
top: 38px;
|
||||
position: absolute;
|
||||
text-align: left;
|
||||
width: 100%;
|
||||
transition: all 0.15s linear 0s;
|
||||
}
|
||||
/* #hoverable Class Styles */
|
||||
.hoverable {
|
||||
position: static;
|
||||
}
|
||||
.hoverable > a:after {
|
||||
content: "\25BC";
|
||||
font-size: 10px;
|
||||
padding-left: 6px;
|
||||
position: relative;
|
||||
top: -1px;
|
||||
}
|
||||
.hoverable:hover .mega-menu {
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
z-index: 900;
|
||||
}
|
||||
|
|
|
@ -17,8 +17,8 @@
|
|||
<div class='flex flex-col'>
|
||||
<span>{{ default_billing.name }}</span>
|
||||
<span>{{ default_billing.company }}</span>
|
||||
<span>{{ default_billing.street }}</span>
|
||||
<span>{{ default_billing.boxapt }}</span>
|
||||
<span>{{ default_billing.addressLine1 }}</span>
|
||||
<span>{{ default_billing.addressLine2 }}</span>
|
||||
<span>{{ default_billing.city }}, {{ default_billing.state }} {{ default_billing.zip }}</span>
|
||||
<span>{{ default_billing.phone }}</span>
|
||||
</div>
|
||||
|
|
|
@ -40,8 +40,8 @@
|
|||
<div class="flex flex-col gap-1">
|
||||
<h4 class="font-semibold">{{ default_shipping.name }}</h4>
|
||||
<h4 class="font-semibold">{{ default_shipping.company }}</h4>
|
||||
<h4 class="font-semibold">{{ default_shipping.street }}</h4>
|
||||
<h4 class="font-semibold">{{ default_billing.boxapt }}</h4>
|
||||
<h4 class="font-semibold">{{ default_shipping.addressLine1 }}</h4>
|
||||
<h4 class="font-semibold">{{ default_billing.addressLine2 }}</h4>
|
||||
<h4 class="font-semibold">{{ default_shipping.city }}, {{ default_shipping.state }}, {{ default_shipping.zip }}</h4>
|
||||
<h4 class="font-semibold">{{ default_shipping.phone }}</h4>
|
||||
</div>
|
||||
|
@ -55,8 +55,8 @@
|
|||
<div class="flex flex-col gap-1">
|
||||
<h4 class="font-semibold">{{ default_billing.name }}</h4>
|
||||
<h4 class="font-semibold">{{ default_billing.company }}</h4>
|
||||
<h4 class="font-semibold">{{ default_billing.street }}</h4>
|
||||
<h4 class="font-semibold">{{ default_billing.boxapt }}</h4>
|
||||
<h4 class="font-semibold">{{ default_billing.addressLine1 }}</h4>
|
||||
<h4 class="font-semibold">{{ default_billing.addressLine2 }}</h4>
|
||||
<h4 class="font-semibold">{{ default_billing.city }}, {{ default_billing.state }}, {{ default_billing.zip }}</h4>
|
||||
<h4 class="font-semibold">{{ default_billing.phone }}</h4>
|
||||
</div>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
{% include 'lib/rule.twig' %}
|
||||
</div>
|
||||
{% include 'lib/alert.twig' %}
|
||||
<form action="/magic-link" method="get" class="flex flex-col gap-4">
|
||||
<form action="/account/login" method="post" class="flex flex-col gap-4">
|
||||
{% include 'lib/input.twig' with {
|
||||
type: 'email',
|
||||
name: 'email',
|
||||
|
|
|
@ -8,8 +8,8 @@
|
|||
<div class='flex flex-col'>
|
||||
<span>{{ default_shipping.name }}</span>
|
||||
<span>{{ default_shipping.company }}</span>
|
||||
<span>{{ default_shipping.street }}</span>
|
||||
<span>{{ default_shipping.boxapt }}</span>
|
||||
<span>{{ default_shipping.addressLine1 }}</span>
|
||||
<span>{{ default_shipping.addressLine2 }}</span>
|
||||
<span>{{ default_shipping.city }}, {{ default_shipping.state }} {{ default_shipping.zip }}</span>
|
||||
<span>{{ default_shipping.phone }}</span>
|
||||
</div>
|
||||
|
|
|
@ -34,9 +34,9 @@
|
|||
{% include 'lib/form/address.twig' with {
|
||||
action: 'shipping',
|
||||
name: session.last_post.shipping_name,
|
||||
street: session.last_post.shipping_street,
|
||||
addressLine1: session.last_post.shipping_addressLine1,
|
||||
company: session.last_post.shipping_company,
|
||||
boxapt: session.last_post.shipping_boxapt,
|
||||
addressLine2: session.last_post.shipping_addressLine2,
|
||||
city: session.last_post.shipping_city,
|
||||
state: session.last_post.shipping_state,
|
||||
zip: session.last_post.shipping_zip,
|
||||
|
@ -51,16 +51,17 @@
|
|||
</h4>
|
||||
{% include 'lib/toggle.twig' with {
|
||||
label: 'Same as shipping',
|
||||
name: 'use_shipping'
|
||||
name: 'use_shipping',
|
||||
on: true
|
||||
} %}
|
||||
</div>
|
||||
<div id="billing-address" style="display: none;">
|
||||
{% include 'lib/form/address.twig' with {
|
||||
action: 'billing',
|
||||
name: session.last_post.billing_name,
|
||||
street: session.last_post.billing_street,
|
||||
addressLine1: session.last_post.billing_addressLine1,
|
||||
company: session.last_post.billing_company,
|
||||
boxapt: session.last_post.billing_boxapt,
|
||||
addressLine2: session.last_post.billing_addressLine2,
|
||||
city: session.last_post.billing_city,
|
||||
state: session.last_post.billing_state,
|
||||
zip: session.last_post.billing_zip,
|
||||
|
|
|
@ -1,3 +1,34 @@
|
|||
<style>
|
||||
/* #Mega Menu Styles
|
||||
–––––––––––––––––––––––––––––––––––––––––––––––––– */
|
||||
.mega-menu {
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
z-index: -900;
|
||||
left: 0;
|
||||
top: 38px;
|
||||
position: absolute;
|
||||
text-align: left;
|
||||
width: 100%;
|
||||
transition: all 0.15s linear 0s;
|
||||
}
|
||||
/* #hoverable Class Styles */
|
||||
.hoverable {
|
||||
position: static;
|
||||
}
|
||||
.hoverable > a:after {
|
||||
content: "\25BC";
|
||||
font-size: 10px;
|
||||
padding-left: 6px;
|
||||
position: relative;
|
||||
top: -1px;
|
||||
}
|
||||
.hoverable:hover .mega-menu {
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
z-index: 900;
|
||||
}
|
||||
</style>
|
||||
<header class="flex flex-col items-center w-full gap-3 mb-8">
|
||||
<div class="{{ colors.header.banner }} py-1 text-sm flex w-full justify-center">
|
||||
<div class="w-[97%] lg:w-[90%] xl:w-4/5 flex justify-between">
|
||||
|
|
|
@ -14,16 +14,16 @@
|
|||
} %}
|
||||
{% include 'lib/input.twig' with {
|
||||
type: 'text',
|
||||
name: action ~ '_street',
|
||||
label: 'Street',
|
||||
value: street
|
||||
name: action ~ '_addressLine1',
|
||||
label: 'Address Line 1',
|
||||
value: addressLine1
|
||||
} %}
|
||||
{% include 'lib/input.twig' with {
|
||||
type: 'text',
|
||||
name: action ~ '_boxapt',
|
||||
label: 'PO Box/Apt#',
|
||||
name: action ~ '_addressLine2',
|
||||
label: 'Address Line 2',
|
||||
optional: true,
|
||||
value: boxapt
|
||||
value: addressLine2
|
||||
} %}
|
||||
<div class="flex gap-4">
|
||||
{% include 'lib/input.twig' with {
|
||||
|
|
Loading…
Reference in a new issue