This is a simple ticket checkout system to pay with SATS.
It use Lightning Network to pay tickets, NOSTR to comunication, Sendy to mailing service and SQLite to database.
- Copy the
.env.example
file to.env
and fill in the required values.
cp .env.example .env
- Install dependencies
pnmp install
- Use the correct node version
nvm use
- Create database
pnpm prisma migrate dev --init
- Start the server
pnpm dev
your_ticketing_domain/api/ticket/request
- Create user in the database (If the email is not already in the database)
- Create a new ticket in the database
- Add email to Sendy list (Subscribed or not to newsletter)
{
"fullname": <string>,
"email": <string>,
"qty": <number>,
"isSubscribed": <boolean>
}
{
"status": <boolean>,
"data": {
"pr": <string, invoice to pay>,
"orderReferenceId": <64-character lowercase hex value, tag e of zap request>,
"qty": <number, quantity of orders>,
"totalMiliSats": <number, total to pay in mili sats>
}
}
{
"status": <boolean>,
"errors": <array of json objects, each one object describe one error>
}
your_ticketing_domain/api/ticket/claim
- Check if the invoice is paid
- Update database to mark the ticket as paid
{
"fullname": <string>,
"email": <string>,
"zapReceipt": <json object zap receipt nostr event>,
}
{
"status": <boolean>,
"data": {
"fullname": <string>,
"email": <string>,
"orderReferenceId": <64-character lowercase hex value>,
"qty": <number>,
"totalMiliSats": <number>
}
}
your_ticketing_domain/api/ticket/orders
- Validate if you are an authorized admin
Signed Nostr Event with your admin key
{
"id": <32-bytes lowercase hex-encoded sha256 of the serialized event data>,
"pubkey": <32-bytes lowercase hex-encoded public key of the event creator>,
"created_at": <unix timestamp in seconds>,
"kind": 27242,
"tags": [],
"content": <string>,
"sig": <64-bytes lowercase hex of the signature of the sha256 hash of the serialized event data>
}
Content:
{
"limit": <number, 0 for all or specify te quantity>,
"checked_in": <boolean, optional, not passed means both>
"ticket_id": <string, optional, not passed means all orders>
"email": <string, optional, not passed means all orders>
}
You can combine that you prefer. ei. all orders checked in of X email, only order with X ticket ID.
Data is an array of objects with order information.
{
"status": <boolean>,
"data": [
{
"user": {
"fullname": <string>,
"email": <string>
},
"ticketId": <string>,
"qty": <number>,
"totalMiliSats": <number>,
"paid": <boolean>,
"checkIn": <boolean>
},
...
]
}
{
"status": <boolean>,
"errors": <string>
}
your_ticketing_domain/api/ticket/checkin
- Validate if you are an authorized admin
- Check if the order is paid and check in
Signed Nostr Event with your admin key
{
"id": <32-bytes lowercase hex-encoded sha256 of the serialized event data>,
"pubkey": <32-bytes lowercase hex-encoded public key of the event creator>,
"created_at": <unix timestamp in seconds>,
"kind": 27242,
"tags": [],
"content": <string>,
"sig": <64-bytes lowercase hex of the signature of the sha256 hash of the serialized event data>
}
Content:
{
"ticket_id": <string>,
}
{
"status": <boolean>,
"data": {
"alreadyCheckedIn": <boolean, true if the order already checked>,
"order": {
"id": <string, UUID format,
"referenceId": <64-bytes lowercase hex-encoded string>,
"ticketId": <16-bytes lowercase hex-encoded string>",
"qty": <number>,
"totalMiliSats": <number>,
"paid": <boolean>,
"checkIn": <boolean, true if the order has been checked in>,
"zapReceiptId": <64-bytes lowercase hex-encoded string>,
"userId": <string, UUID format>
},
"user": {
"id": <string, UUID format>,
"fullname": <string>,
"email": <string>
}
{
"status": <boolean>,
"errors": <string>
}