sqldb: Change payment_intent relationship to payment table
Previously a one(intent)-to-many(payment) relationship it is now changed to a one-to-one relationship because a payment request only can have 1 payment related to it. Looking into the future with BOLT12 offers, the fetched invoice from the offer could be stored here as well and the relationship would still hold.
This commit is contained in:
4 files changed
+50
-49
No files matched your search
@@ -2,43 +2,12 @@
|
||||
-- Payment System Schema Migration
|
||||
-- ─────────────────────────────────────────────
|
||||
-- This migration creates the complete payment system schema including:
|
||||
-- - Payment intents (BOLT 11/12 invoices, offers)
|
||||
-- - Payment intents (only BOLT 11 invoices for now)
|
||||
-- - Payment attempts and HTLC tracking
|
||||
-- - Route hops and custom TLV records
|
||||
-- - Resolution tracking for settled/failed payments
|
||||
-- ─────────────────────────────────────────────
|
||||
|
||||
-- ─────────────────────────────────────────────
|
||||
-- Payment Intents Table
|
||||
-- ─────────────────────────────────────────────
|
||||
-- Stores the descriptor of what the payment is paying for.
|
||||
-- Depending on the type, the payload might contain:
|
||||
-- - BOLT 11 invoice data
|
||||
-- - BOLT 12 offer data
|
||||
-- - NULL for legacy hash-only/keysend style payments
|
||||
-- ─────────────────────────────────────────────
|
||||
|
||||
CREATE TABLE IF NOT EXISTS payment_intents (
|
||||
-- Primary key for the intent record
|
||||
id INTEGER PRIMARY KEY,
|
||||
|
||||
-- The type of intent (e.g. 0 = bolt11_invoice, 1 = bolt12_offer)
|
||||
-- Uses SMALLINT (int16) for efficient storage of enum values
|
||||
intent_type SMALLINT NOT NULL,
|
||||
|
||||
-- The serialized payload for the payment intent
|
||||
-- Content depends on type - could be invoice, offer, or NULL
|
||||
intent_payload BLOB
|
||||
);
|
||||
|
||||
-- Index for efficient querying by intent type
|
||||
CREATE INDEX IF NOT EXISTS idx_payment_intents_type
|
||||
ON payment_intents(intent_type);
|
||||
|
||||
-- Unique constraint for deduplication of payment intents
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS idx_payment_intents_unique
|
||||
ON payment_intents(intent_type, intent_payload);
|
||||
|
||||
-- ─────────────────────────────────────────────
|
||||
-- Payments Table
|
||||
-- ─────────────────────────────────────────────
|
||||
@@ -55,10 +24,6 @@ CREATE TABLE IF NOT EXISTS payments (
|
||||
-- Primary key for the payment record
|
||||
id INTEGER PRIMARY KEY,
|
||||
|
||||
-- Optional reference to the payment intent this payment was derived from
|
||||
-- Links to BOLT 11 invoice, BOLT 12 offer, etc.
|
||||
intent_id BIGINT REFERENCES payment_intents (id),
|
||||
|
||||
-- The amount of the payment in millisatoshis
|
||||
amount_msat BIGINT NOT NULL,
|
||||
|
||||
@@ -84,6 +49,45 @@ CREATE TABLE IF NOT EXISTS payments (
|
||||
CREATE INDEX IF NOT EXISTS idx_payments_created_at
|
||||
ON payments(created_at);
|
||||
|
||||
-- ─────────────────────────────────────────────
|
||||
-- Payment Intents Table
|
||||
-- ─────────────────────────────────────────────
|
||||
-- Stores the descriptor of what the payment is paying for.
|
||||
-- Depending on the type, the payload might contain:
|
||||
-- - BOLT 11 invoice data
|
||||
-- - BOLT 12 offer data
|
||||
-- - NULL for legacy hash-only/keysend style payments
|
||||
-- ─────────────────────────────────────────────
|
||||
|
||||
CREATE TABLE IF NOT EXISTS payment_intents (
|
||||
-- Primary key for the intent record
|
||||
id INTEGER PRIMARY KEY,
|
||||
|
||||
-- Reference to the payment this intent belongs to (one-to-one relationship)
|
||||
-- When the payment is deleted, the intent is automatically deleted
|
||||
payment_id BIGINT NOT NULL REFERENCES payments (id) ON DELETE CASCADE,
|
||||
|
||||
-- The type of intent (e.g. 0 = bolt11_invoice, 1 = bolt12_invoice)
|
||||
-- Uses SMALLINT (int16) for efficient storage of enum values
|
||||
intent_type SMALLINT NOT NULL,
|
||||
|
||||
-- The serialized payload for the payment intent
|
||||
-- Content depends on type - could be invoice, offer, or NULL
|
||||
intent_payload BLOB,
|
||||
|
||||
-- Ensure one-to-one relationship: each payment has at most one intent.
|
||||
-- Currently we only support one intent per payment this makes sure we do
|
||||
-- not accidentally pay the same request multiple times. This currently
|
||||
-- only has bolt 11 payment requests/invoices. But in the future this can
|
||||
-- also include BOLT 12 offers/invoices.
|
||||
CONSTRAINT idx_payment_intents_payment_id_unique
|
||||
UNIQUE (payment_id)
|
||||
);
|
||||
|
||||
-- Index for efficient querying by intent type
|
||||
CREATE INDEX IF NOT EXISTS idx_payment_intents_type
|
||||
ON payment_intents(intent_type);
|
||||
|
||||
-- ─────────────────────────────────────────────
|
||||
-- Payment HTLC Attempts Table
|
||||
-- ─────────────────────────────────────────────
|
||||
|
||||
@@ -211,7 +211,6 @@ type MigrationTracker struct {
|
||||
|
||||
type Payment struct {
|
||||
ID int64
|
||||
IntentID sql.NullInt64
|
||||
AmountMsat int64
|
||||
CreatedAt time.Time
|
||||
PaymentIdentifier []byte
|
||||
@@ -264,6 +263,7 @@ type PaymentHtlcAttemptResolution struct {
|
||||
|
||||
type PaymentIntent struct {
|
||||
ID int64
|
||||
PaymentID int64
|
||||
IntentType int16
|
||||
IntentPayload []byte
|
||||
}
|
||||
|
||||
@@ -317,11 +317,11 @@ func (q *Queries) FetchHtlcAttemptsForPayments(ctx context.Context, paymentIds [
|
||||
|
||||
const fetchPayment = `-- name: FetchPayment :one
|
||||
SELECT
|
||||
p.id, p.intent_id, p.amount_msat, p.created_at, p.payment_identifier, p.fail_reason,
|
||||
p.id, p.amount_msat, p.created_at, p.payment_identifier, p.fail_reason,
|
||||
i.intent_type AS "intent_type",
|
||||
i.intent_payload AS "intent_payload"
|
||||
FROM payments p
|
||||
LEFT JOIN payment_intents i ON i.id = p.intent_id
|
||||
LEFT JOIN payment_intents i ON i.payment_id = p.id
|
||||
WHERE p.payment_identifier = $1
|
||||
`
|
||||
|
||||
@@ -336,7 +336,6 @@ func (q *Queries) FetchPayment(ctx context.Context, paymentIdentifier []byte) (F
|
||||
var i FetchPaymentRow
|
||||
err := row.Scan(
|
||||
&i.Payment.ID,
|
||||
&i.Payment.IntentID,
|
||||
&i.Payment.AmountMsat,
|
||||
&i.Payment.CreatedAt,
|
||||
&i.Payment.PaymentIdentifier,
|
||||
@@ -398,11 +397,11 @@ func (q *Queries) FetchPaymentLevelFirstHopCustomRecords(ctx context.Context, pa
|
||||
|
||||
const fetchPaymentsByIDs = `-- name: FetchPaymentsByIDs :many
|
||||
SELECT
|
||||
p.id, p.intent_id, p.amount_msat, p.created_at, p.payment_identifier, p.fail_reason,
|
||||
p.id, p.amount_msat, p.created_at, p.payment_identifier, p.fail_reason,
|
||||
i.intent_type AS "intent_type",
|
||||
i.intent_payload AS "intent_payload"
|
||||
FROM payments p
|
||||
LEFT JOIN payment_intents i ON i.id = p.intent_id
|
||||
LEFT JOIN payment_intents i ON i.payment_id = p.id
|
||||
WHERE p.id IN (/*SLICE:payment_ids*/?)
|
||||
`
|
||||
|
||||
@@ -433,7 +432,6 @@ func (q *Queries) FetchPaymentsByIDs(ctx context.Context, paymentIds []int64) ([
|
||||
var i FetchPaymentsByIDsRow
|
||||
if err := rows.Scan(
|
||||
&i.Payment.ID,
|
||||
&i.Payment.IntentID,
|
||||
&i.Payment.AmountMsat,
|
||||
&i.Payment.CreatedAt,
|
||||
&i.Payment.PaymentIdentifier,
|
||||
@@ -510,11 +508,11 @@ const filterPayments = `-- name: FilterPayments :many
|
||||
*/
|
||||
|
||||
SELECT
|
||||
p.id, p.intent_id, p.amount_msat, p.created_at, p.payment_identifier, p.fail_reason,
|
||||
p.id, p.amount_msat, p.created_at, p.payment_identifier, p.fail_reason,
|
||||
i.intent_type AS "intent_type",
|
||||
i.intent_payload AS "intent_payload"
|
||||
FROM payments p
|
||||
LEFT JOIN payment_intents i ON i.id = p.intent_id
|
||||
LEFT JOIN payment_intents i ON i.payment_id = p.id
|
||||
WHERE (
|
||||
p.id > $1 OR
|
||||
$1 IS NULL
|
||||
@@ -572,7 +570,6 @@ func (q *Queries) FilterPayments(ctx context.Context, arg FilterPaymentsParams)
|
||||
var i FilterPaymentsRow
|
||||
if err := rows.Scan(
|
||||
&i.Payment.ID,
|
||||
&i.Payment.IntentID,
|
||||
&i.Payment.AmountMsat,
|
||||
&i.Payment.CreatedAt,
|
||||
&i.Payment.PaymentIdentifier,
|
||||
|
||||
@@ -9,7 +9,7 @@ SELECT
|
||||
i.intent_type AS "intent_type",
|
||||
i.intent_payload AS "intent_payload"
|
||||
FROM payments p
|
||||
LEFT JOIN payment_intents i ON i.id = p.intent_id
|
||||
LEFT JOIN payment_intents i ON i.payment_id = p.id
|
||||
WHERE (
|
||||
p.id > sqlc.narg('index_offset_get') OR
|
||||
sqlc.narg('index_offset_get') IS NULL
|
||||
@@ -37,7 +37,7 @@ SELECT
|
||||
i.intent_type AS "intent_type",
|
||||
i.intent_payload AS "intent_payload"
|
||||
FROM payments p
|
||||
LEFT JOIN payment_intents i ON i.id = p.intent_id
|
||||
LEFT JOIN payment_intents i ON i.payment_id = p.id
|
||||
WHERE p.payment_identifier = $1;
|
||||
|
||||
-- name: FetchPaymentsByIDs :many
|
||||
@@ -46,7 +46,7 @@ SELECT
|
||||
i.intent_type AS "intent_type",
|
||||
i.intent_payload AS "intent_payload"
|
||||
FROM payments p
|
||||
LEFT JOIN payment_intents i ON i.id = p.intent_id
|
||||
LEFT JOIN payment_intents i ON i.payment_id = p.id
|
||||
WHERE p.id IN (sqlc.slice('payment_ids')/*SLICE:payment_ids*/);
|
||||
|
||||
-- name: CountPayments :one
|
||||
|
||||
Reference in new issue
Block a user