list offer ops

This commit is contained in:
boufni95 2024-12-06 19:24:02 +00:00
parent babce0d4da
commit 299f5d86b6
13 changed files with 307 additions and 7 deletions

View file

@ -168,6 +168,11 @@ The nostr server will send back a message response, and inside the body there wi
- input: [OfferId](#OfferId) - input: [OfferId](#OfferId)
- output: [OfferConfig](#OfferConfig) - output: [OfferConfig](#OfferConfig)
- GetUserOfferInvoices
- auth type: __User__
- input: [GetUserOfferInvoicesReq](#GetUserOfferInvoicesReq)
- output: [OfferInvoices](#OfferInvoices)
- GetUserOffers - GetUserOffers
- auth type: __User__ - auth type: __User__
- This methods has an __empty__ __request__ body - This methods has an __empty__ __request__ body
@ -585,6 +590,13 @@ The nostr server will send back a message response, and inside the body there wi
- input: [OfferId](#OfferId) - input: [OfferId](#OfferId)
- output: [OfferConfig](#OfferConfig) - output: [OfferConfig](#OfferConfig)
- GetUserOfferInvoices
- auth type: __User__
- http method: __post__
- http route: __/api/user/offer/get/invoices__
- input: [GetUserOfferInvoicesReq](#GetUserOfferInvoicesReq)
- output: [OfferInvoices](#OfferInvoices)
- GetUserOffers - GetUserOffers
- auth type: __User__ - auth type: __User__
- http method: __get__ - http method: __get__
@ -998,6 +1010,10 @@ The nostr server will send back a message response, and inside the body there wi
### GetProductBuyLinkResponse ### GetProductBuyLinkResponse
- __link__: _string_ - __link__: _string_
### GetUserOfferInvoicesReq
- __include_unpaid__: _boolean_
- __offer_id__: _string_
### GetUserOperationsRequest ### GetUserOperationsRequest
- __latestIncomingInvoice__: _number_ - __latestIncomingInvoice__: _number_
- __latestIncomingTx__: _number_ - __latestIncomingTx__: _number_
@ -1130,6 +1146,16 @@ The nostr server will send back a message response, and inside the body there wi
### OfferId ### OfferId
- __offer_id__: _string_ - __offer_id__: _string_
### OfferInvoice
- __amount__: _number_
- __data__: MAP with key: _string_ and value: _string_
- __invoice__: _string_
- __offer_id__: _string_
- __paid_at_unix__: _number_
### OfferInvoices
- __invoices__: ARRAY of: _[OfferInvoice](#OfferInvoice)_
### OpenChannel ### OpenChannel
- __active__: _boolean_ - __active__: _boolean_
- __capacity__: _number_ - __capacity__: _number_

View file

@ -95,6 +95,7 @@ type Client struct {
GetUsageMetrics func() (*UsageMetrics, error) GetUsageMetrics func() (*UsageMetrics, error)
GetUserInfo func() (*UserInfo, error) GetUserInfo func() (*UserInfo, error)
GetUserOffer func(req OfferId) (*OfferConfig, error) GetUserOffer func(req OfferId) (*OfferConfig, error)
GetUserOfferInvoices func(req GetUserOfferInvoicesReq) (*OfferInvoices, error)
GetUserOffers func() (*UserOffers, error) GetUserOffers func() (*UserOffers, error)
GetUserOperations func(req GetUserOperationsRequest) (*GetUserOperationsResponse, error) GetUserOperations func(req GetUserOperationsRequest) (*GetUserOperationsResponse, error)
HandleLnurlAddress func(routeParams HandleLnurlAddress_RouteParams) (*LnurlPayInfoResponse, error) HandleLnurlAddress func(routeParams HandleLnurlAddress_RouteParams) (*LnurlPayInfoResponse, error)
@ -1110,6 +1111,35 @@ func NewClient(params ClientParams) *Client {
} }
return &res, nil return &res, nil
}, },
GetUserOfferInvoices: func(req GetUserOfferInvoicesReq) (*OfferInvoices, error) {
auth, err := params.RetrieveUserAuth()
if err != nil {
return nil, err
}
finalRoute := "/api/user/offer/get/invoices"
body, err := json.Marshal(req)
if err != nil {
return nil, err
}
resBody, err := doPostRequest(params.BaseURL+finalRoute, body, auth)
if err != nil {
return nil, err
}
result := ResultError{}
err = json.Unmarshal(resBody, &result)
if err != nil {
return nil, err
}
if result.Status == "ERROR" {
return nil, fmt.Errorf(result.Reason)
}
res := OfferInvoices{}
err = json.Unmarshal(resBody, &res)
if err != nil {
return nil, err
}
return &res, nil
},
GetUserOffers: func() (*UserOffers, error) { GetUserOffers: func() (*UserOffers, error) {
auth, err := params.RetrieveUserAuth() auth, err := params.RetrieveUserAuth()
if err != nil { if err != nil {

View file

@ -274,6 +274,10 @@ type GetPaymentStateRequest struct {
type GetProductBuyLinkResponse struct { type GetProductBuyLinkResponse struct {
Link string `json:"link"` Link string `json:"link"`
} }
type GetUserOfferInvoicesReq struct {
Include_unpaid bool `json:"include_unpaid"`
Offer_id string `json:"offer_id"`
}
type GetUserOperationsRequest struct { type GetUserOperationsRequest struct {
Latestincominginvoice int64 `json:"latestIncomingInvoice"` Latestincominginvoice int64 `json:"latestIncomingInvoice"`
Latestincomingtx int64 `json:"latestIncomingTx"` Latestincomingtx int64 `json:"latestIncomingTx"`
@ -406,6 +410,16 @@ type OfferConfig struct {
type OfferId struct { type OfferId struct {
Offer_id string `json:"offer_id"` Offer_id string `json:"offer_id"`
} }
type OfferInvoice struct {
Amount int64 `json:"amount"`
Data map[string]string `json:"data"`
Invoice string `json:"invoice"`
Offer_id string `json:"offer_id"`
Paid_at_unix int64 `json:"paid_at_unix"`
}
type OfferInvoices struct {
Invoices []OfferInvoice `json:"invoices"`
}
type OpenChannel struct { type OpenChannel struct {
Active bool `json:"active"` Active bool `json:"active"`
Capacity int64 `json:"capacity"` Capacity int64 `json:"capacity"`

View file

@ -467,6 +467,18 @@ export default (methods: Types.ServerMethods, opts: ServerOptions) => {
callsMetrics.push({ ...opInfo, ...opStats, ...ctx }) callsMetrics.push({ ...opInfo, ...opStats, ...ctx })
} }
break break
case 'GetUserOfferInvoices':
if (!methods.GetUserOfferInvoices) {
throw new Error('method GetUserOfferInvoices not found' )
} else {
const error = Types.GetUserOfferInvoicesReqValidate(operation.req)
opStats.validate = process.hrtime.bigint()
if (error !== null) throw error
const res = await methods.GetUserOfferInvoices({...operation, ctx}); responses.push({ status: 'OK', ...res })
opStats.handle = process.hrtime.bigint()
callsMetrics.push({ ...opInfo, ...opStats, ...ctx })
}
break
case 'GetUserOffers': case 'GetUserOffers':
if (!methods.GetUserOffers) { if (!methods.GetUserOffers) {
throw new Error('method GetUserOffers not found' ) throw new Error('method GetUserOffers not found' )
@ -1135,6 +1147,28 @@ export default (methods: Types.ServerMethods, opts: ServerOptions) => {
opts.metricsCallback([{ ...info, ...stats, ...authContext }]) opts.metricsCallback([{ ...info, ...stats, ...authContext }])
} catch (ex) { const e = ex as any; logErrorAndReturnResponse(e, e.message || e, res, logger, { ...info, ...stats, ...authCtx }, opts.metricsCallback); if (opts.throwErrors) throw e } } catch (ex) { const e = ex as any; logErrorAndReturnResponse(e, e.message || e, res, logger, { ...info, ...stats, ...authCtx }, opts.metricsCallback); if (opts.throwErrors) throw e }
}) })
if (!opts.allowNotImplementedMethods && !methods.GetUserOfferInvoices) throw new Error('method: GetUserOfferInvoices is not implemented')
app.post('/api/user/offer/get/invoices', async (req, res) => {
const info: Types.RequestInfo = { rpcName: 'GetUserOfferInvoices', batch: false, nostr: false, batchSize: 0}
const stats: Types.RequestStats = { startMs:req.startTimeMs || 0, start:req.startTime || 0n, parse: process.hrtime.bigint(), guard: 0n, validate: 0n, handle: 0n }
let authCtx: Types.AuthContext = {}
try {
if (!methods.GetUserOfferInvoices) throw new Error('method: GetUserOfferInvoices is not implemented')
const authContext = await opts.UserAuthGuard(req.headers['authorization'])
authCtx = authContext
stats.guard = process.hrtime.bigint()
const request = req.body
const error = Types.GetUserOfferInvoicesReqValidate(request)
stats.validate = process.hrtime.bigint()
if (error !== null) return logErrorAndReturnResponse(error, 'invalid request body', res, logger, { ...info, ...stats, ...authContext }, opts.metricsCallback)
const query = req.query
const params = req.params
const response = await methods.GetUserOfferInvoices({rpcName:'GetUserOfferInvoices', ctx:authContext , req: request})
stats.handle = process.hrtime.bigint()
res.json({status: 'OK', ...response})
opts.metricsCallback([{ ...info, ...stats, ...authContext }])
} catch (ex) { const e = ex as any; logErrorAndReturnResponse(e, e.message || e, res, logger, { ...info, ...stats, ...authCtx }, opts.metricsCallback); if (opts.throwErrors) throw e }
})
if (!opts.allowNotImplementedMethods && !methods.GetUserOffers) throw new Error('method: GetUserOffers is not implemented') if (!opts.allowNotImplementedMethods && !methods.GetUserOffers) throw new Error('method: GetUserOffers is not implemented')
app.get('/api/user/offers/get', async (req, res) => { app.get('/api/user/offers/get', async (req, res) => {
const info: Types.RequestInfo = { rpcName: 'GetUserOffers', batch: false, nostr: false, batchSize: 0} const info: Types.RequestInfo = { rpcName: 'GetUserOffers', batch: false, nostr: false, batchSize: 0}

View file

@ -522,6 +522,20 @@ export default (params: ClientParams) => ({
} }
return { status: 'ERROR', reason: 'invalid response' } return { status: 'ERROR', reason: 'invalid response' }
}, },
GetUserOfferInvoices: async (request: Types.GetUserOfferInvoicesReq): Promise<ResultError | ({ status: 'OK' }& Types.OfferInvoices)> => {
const auth = await params.retrieveUserAuth()
if (auth === null) throw new Error('retrieveUserAuth() returned null')
let finalRoute = '/api/user/offer/get/invoices'
const { data } = await axios.post(params.baseUrl + finalRoute, request, { headers: { 'authorization': auth } })
if (data.status === 'ERROR' && typeof data.reason === 'string') return data
if (data.status === 'OK') {
const result = data
if(!params.checkResult) return { status: 'OK', ...result }
const error = Types.OfferInvoicesValidate(result)
if (error === null) { return { status: 'OK', ...result } } else return { status: 'ERROR', reason: error.message }
}
return { status: 'ERROR', reason: 'invalid response' }
},
GetUserOffers: async (): Promise<ResultError | ({ status: 'OK' }& Types.UserOffers)> => { GetUserOffers: async (): Promise<ResultError | ({ status: 'OK' }& Types.UserOffers)> => {
const auth = await params.retrieveUserAuth() const auth = await params.retrieveUserAuth()
if (auth === null) throw new Error('retrieveUserAuth() returned null') if (auth === null) throw new Error('retrieveUserAuth() returned null')

View file

@ -451,6 +451,21 @@ export default (params: NostrClientParams, send: (to:string, message: NostrRequ
} }
return { status: 'ERROR', reason: 'invalid response' } return { status: 'ERROR', reason: 'invalid response' }
}, },
GetUserOfferInvoices: async (request: Types.GetUserOfferInvoicesReq): Promise<ResultError | ({ status: 'OK' }& Types.OfferInvoices)> => {
const auth = await params.retrieveNostrUserAuth()
if (auth === null) throw new Error('retrieveNostrUserAuth() returned null')
const nostrRequest: NostrRequest = {}
nostrRequest.body = request
const data = await send(params.pubDestination, {rpcName:'GetUserOfferInvoices',authIdentifier:auth, ...nostrRequest })
if (data.status === 'ERROR' && typeof data.reason === 'string') return data
if (data.status === 'OK') {
const result = data
if(!params.checkResult) return { status: 'OK', ...result }
const error = Types.OfferInvoicesValidate(result)
if (error === null) { return { status: 'OK', ...result } } else return { status: 'ERROR', reason: error.message }
}
return { status: 'ERROR', reason: 'invalid response' }
},
GetUserOffers: async (): Promise<ResultError | ({ status: 'OK' }& Types.UserOffers)> => { GetUserOffers: async (): Promise<ResultError | ({ status: 'OK' }& Types.UserOffers)> => {
const auth = await params.retrieveNostrUserAuth() const auth = await params.retrieveNostrUserAuth()
if (auth === null) throw new Error('retrieveNostrUserAuth() returned null') if (auth === null) throw new Error('retrieveNostrUserAuth() returned null')

View file

@ -349,6 +349,18 @@ export default (methods: Types.ServerMethods, opts: NostrOptions) => {
callsMetrics.push({ ...opInfo, ...opStats, ...ctx }) callsMetrics.push({ ...opInfo, ...opStats, ...ctx })
} }
break break
case 'GetUserOfferInvoices':
if (!methods.GetUserOfferInvoices) {
throw new Error('method not defined: GetUserOfferInvoices')
} else {
const error = Types.GetUserOfferInvoicesReqValidate(operation.req)
opStats.validate = process.hrtime.bigint()
if (error !== null) throw error
const res = await methods.GetUserOfferInvoices({...operation, ctx}); responses.push({ status: 'OK', ...res })
opStats.handle = process.hrtime.bigint()
callsMetrics.push({ ...opInfo, ...opStats, ...ctx })
}
break
case 'GetUserOffers': case 'GetUserOffers':
if (!methods.GetUserOffers) { if (!methods.GetUserOffers) {
throw new Error('method not defined: GetUserOffers') throw new Error('method not defined: GetUserOffers')
@ -816,6 +828,22 @@ export default (methods: Types.ServerMethods, opts: NostrOptions) => {
opts.metricsCallback([{ ...info, ...stats, ...authContext }]) opts.metricsCallback([{ ...info, ...stats, ...authContext }])
}catch(ex){ const e = ex as any; logErrorAndReturnResponse(e, e.message || e, res, logger, { ...info, ...stats, ...authCtx }, opts.metricsCallback); if (opts.throwErrors) throw e } }catch(ex){ const e = ex as any; logErrorAndReturnResponse(e, e.message || e, res, logger, { ...info, ...stats, ...authCtx }, opts.metricsCallback); if (opts.throwErrors) throw e }
break break
case 'GetUserOfferInvoices':
try {
if (!methods.GetUserOfferInvoices) throw new Error('method: GetUserOfferInvoices is not implemented')
const authContext = await opts.NostrUserAuthGuard(req.appId, req.authIdentifier)
stats.guard = process.hrtime.bigint()
authCtx = authContext
const request = req.body
const error = Types.GetUserOfferInvoicesReqValidate(request)
stats.validate = process.hrtime.bigint()
if (error !== null) return logErrorAndReturnResponse(error, 'invalid request body', res, logger, { ...info, ...stats, ...authCtx }, opts.metricsCallback)
const response = await methods.GetUserOfferInvoices({rpcName:'GetUserOfferInvoices', ctx:authContext , req: request})
stats.handle = process.hrtime.bigint()
res({status: 'OK', ...response})
opts.metricsCallback([{ ...info, ...stats, ...authContext }])
}catch(ex){ const e = ex as any; logErrorAndReturnResponse(e, e.message || e, res, logger, { ...info, ...stats, ...authCtx }, opts.metricsCallback); if (opts.throwErrors) throw e }
break
case 'GetUserOffers': case 'GetUserOffers':
try { try {
if (!methods.GetUserOffers) throw new Error('method: GetUserOffers is not implemented') if (!methods.GetUserOffers) throw new Error('method: GetUserOffers is not implemented')

View file

@ -34,8 +34,8 @@ export type UserContext = {
app_user_id: string app_user_id: string
user_id: string user_id: string
} }
export type UserMethodInputs = AddProduct_Input | AddUserOffer_Input | AuthorizeDebit_Input | BanDebit_Input | DecodeInvoice_Input | DeleteUserOffer_Input | EditDebit_Input | EnrollAdminToken_Input | GetDebitAuthorizations_Input | GetLNURLChannelLink_Input | GetLnurlPayLink_Input | GetLnurlWithdrawLink_Input | GetPaymentState_Input | GetUserInfo_Input | GetUserOffer_Input | GetUserOffers_Input | GetUserOperations_Input | NewAddress_Input | NewInvoice_Input | NewProductInvoice_Input | PayAddress_Input | PayInvoice_Input | ResetDebit_Input | RespondToDebit_Input | UpdateCallbackUrl_Input | UpdateUserOffer_Input | UserHealth_Input export type UserMethodInputs = AddProduct_Input | AddUserOffer_Input | AuthorizeDebit_Input | BanDebit_Input | DecodeInvoice_Input | DeleteUserOffer_Input | EditDebit_Input | EnrollAdminToken_Input | GetDebitAuthorizations_Input | GetLNURLChannelLink_Input | GetLnurlPayLink_Input | GetLnurlWithdrawLink_Input | GetPaymentState_Input | GetUserInfo_Input | GetUserOffer_Input | GetUserOfferInvoices_Input | GetUserOffers_Input | GetUserOperations_Input | NewAddress_Input | NewInvoice_Input | NewProductInvoice_Input | PayAddress_Input | PayInvoice_Input | ResetDebit_Input | RespondToDebit_Input | UpdateCallbackUrl_Input | UpdateUserOffer_Input | UserHealth_Input
export type UserMethodOutputs = AddProduct_Output | AddUserOffer_Output | AuthorizeDebit_Output | BanDebit_Output | DecodeInvoice_Output | DeleteUserOffer_Output | EditDebit_Output | EnrollAdminToken_Output | GetDebitAuthorizations_Output | GetLNURLChannelLink_Output | GetLnurlPayLink_Output | GetLnurlWithdrawLink_Output | GetPaymentState_Output | GetUserInfo_Output | GetUserOffer_Output | GetUserOffers_Output | GetUserOperations_Output | NewAddress_Output | NewInvoice_Output | NewProductInvoice_Output | PayAddress_Output | PayInvoice_Output | ResetDebit_Output | RespondToDebit_Output | UpdateCallbackUrl_Output | UpdateUserOffer_Output | UserHealth_Output export type UserMethodOutputs = AddProduct_Output | AddUserOffer_Output | AuthorizeDebit_Output | BanDebit_Output | DecodeInvoice_Output | DeleteUserOffer_Output | EditDebit_Output | EnrollAdminToken_Output | GetDebitAuthorizations_Output | GetLNURLChannelLink_Output | GetLnurlPayLink_Output | GetLnurlWithdrawLink_Output | GetPaymentState_Output | GetUserInfo_Output | GetUserOffer_Output | GetUserOfferInvoices_Output | GetUserOffers_Output | GetUserOperations_Output | NewAddress_Output | NewInvoice_Output | NewProductInvoice_Output | PayAddress_Output | PayInvoice_Output | ResetDebit_Output | RespondToDebit_Output | UpdateCallbackUrl_Output | UpdateUserOffer_Output | UserHealth_Output
export type AuthContext = AdminContext | AppContext | GuestContext | GuestWithPubContext | MetricsContext | UserContext export type AuthContext = AdminContext | AppContext | GuestContext | GuestWithPubContext | MetricsContext | UserContext
export type AddApp_Input = {rpcName:'AddApp', req: AddAppRequest} export type AddApp_Input = {rpcName:'AddApp', req: AddAppRequest}
@ -167,6 +167,9 @@ export type GetUserInfo_Output = ResultError | ({ status: 'OK' } & UserInfo)
export type GetUserOffer_Input = {rpcName:'GetUserOffer', req: OfferId} export type GetUserOffer_Input = {rpcName:'GetUserOffer', req: OfferId}
export type GetUserOffer_Output = ResultError | ({ status: 'OK' } & OfferConfig) export type GetUserOffer_Output = ResultError | ({ status: 'OK' } & OfferConfig)
export type GetUserOfferInvoices_Input = {rpcName:'GetUserOfferInvoices', req: GetUserOfferInvoicesReq}
export type GetUserOfferInvoices_Output = ResultError | ({ status: 'OK' } & OfferInvoices)
export type GetUserOffers_Input = {rpcName:'GetUserOffers'} export type GetUserOffers_Input = {rpcName:'GetUserOffers'}
export type GetUserOffers_Output = ResultError | ({ status: 'OK' } & UserOffers) export type GetUserOffers_Output = ResultError | ({ status: 'OK' } & UserOffers)
@ -314,6 +317,7 @@ export type ServerMethods = {
GetUsageMetrics?: (req: GetUsageMetrics_Input & {ctx: MetricsContext }) => Promise<UsageMetrics> GetUsageMetrics?: (req: GetUsageMetrics_Input & {ctx: MetricsContext }) => Promise<UsageMetrics>
GetUserInfo?: (req: GetUserInfo_Input & {ctx: UserContext }) => Promise<UserInfo> GetUserInfo?: (req: GetUserInfo_Input & {ctx: UserContext }) => Promise<UserInfo>
GetUserOffer?: (req: GetUserOffer_Input & {ctx: UserContext }) => Promise<OfferConfig> GetUserOffer?: (req: GetUserOffer_Input & {ctx: UserContext }) => Promise<OfferConfig>
GetUserOfferInvoices?: (req: GetUserOfferInvoices_Input & {ctx: UserContext }) => Promise<OfferInvoices>
GetUserOffers?: (req: GetUserOffers_Input & {ctx: UserContext }) => Promise<UserOffers> GetUserOffers?: (req: GetUserOffers_Input & {ctx: UserContext }) => Promise<UserOffers>
GetUserOperations?: (req: GetUserOperations_Input & {ctx: UserContext }) => Promise<GetUserOperationsResponse> GetUserOperations?: (req: GetUserOperations_Input & {ctx: UserContext }) => Promise<GetUserOperationsResponse>
HandleLnurlAddress?: (req: HandleLnurlAddress_Input & {ctx: GuestContext }) => Promise<LnurlPayInfoResponse> HandleLnurlAddress?: (req: HandleLnurlAddress_Input & {ctx: GuestContext }) => Promise<LnurlPayInfoResponse>
@ -1504,6 +1508,29 @@ export const GetProductBuyLinkResponseValidate = (o?: GetProductBuyLinkResponse,
return null return null
} }
export type GetUserOfferInvoicesReq = {
include_unpaid: boolean
offer_id: string
}
export const GetUserOfferInvoicesReqOptionalFields: [] = []
export type GetUserOfferInvoicesReqOptions = OptionsBaseMessage & {
checkOptionalsAreSet?: []
include_unpaid_CustomCheck?: (v: boolean) => boolean
offer_id_CustomCheck?: (v: string) => boolean
}
export const GetUserOfferInvoicesReqValidate = (o?: GetUserOfferInvoicesReq, opts: GetUserOfferInvoicesReqOptions = {}, path: string = 'GetUserOfferInvoicesReq::root.'): Error | null => {
if (opts.checkOptionalsAreSet && opts.allOptionalsAreSet) return new Error(path + ': only one of checkOptionalsAreSet or allOptionalNonDefault can be set for each message')
if (typeof o !== 'object' || o === null) return new Error(path + ': object is not an instance of an object or is null')
if (typeof o.include_unpaid !== 'boolean') return new Error(`${path}.include_unpaid: is not a boolean`)
if (opts.include_unpaid_CustomCheck && !opts.include_unpaid_CustomCheck(o.include_unpaid)) return new Error(`${path}.include_unpaid: custom check failed`)
if (typeof o.offer_id !== 'string') return new Error(`${path}.offer_id: is not a string`)
if (opts.offer_id_CustomCheck && !opts.offer_id_CustomCheck(o.offer_id)) return new Error(`${path}.offer_id: custom check failed`)
return null
}
export type GetUserOperationsRequest = { export type GetUserOperationsRequest = {
latestIncomingInvoice: number latestIncomingInvoice: number
latestIncomingTx: number latestIncomingTx: number
@ -2310,6 +2337,69 @@ export const OfferIdValidate = (o?: OfferId, opts: OfferIdOptions = {}, path: st
return null return null
} }
export type OfferInvoice = {
amount: number
data: Record<string, string>
invoice: string
offer_id: string
paid_at_unix: number
}
export const OfferInvoiceOptionalFields: [] = []
export type OfferInvoiceOptions = OptionsBaseMessage & {
checkOptionalsAreSet?: []
amount_CustomCheck?: (v: number) => boolean
data_CustomCheck?: (v: Record<string, string>) => boolean
invoice_CustomCheck?: (v: string) => boolean
offer_id_CustomCheck?: (v: string) => boolean
paid_at_unix_CustomCheck?: (v: number) => boolean
}
export const OfferInvoiceValidate = (o?: OfferInvoice, opts: OfferInvoiceOptions = {}, path: string = 'OfferInvoice::root.'): Error | null => {
if (opts.checkOptionalsAreSet && opts.allOptionalsAreSet) return new Error(path + ': only one of checkOptionalsAreSet or allOptionalNonDefault can be set for each message')
if (typeof o !== 'object' || o === null) return new Error(path + ': object is not an instance of an object or is null')
if (typeof o.amount !== 'number') return new Error(`${path}.amount: is not a number`)
if (opts.amount_CustomCheck && !opts.amount_CustomCheck(o.amount)) return new Error(`${path}.amount: custom check failed`)
if (typeof o.data !== 'object' || o.data === null) return new Error(`${path}.data: is not an object or is null`)
for (const key in o.data) {
if (typeof o.data[key] !== 'string') return new Error(`${path}.data['${key}']: is not a string`)
}
if (typeof o.invoice !== 'string') return new Error(`${path}.invoice: is not a string`)
if (opts.invoice_CustomCheck && !opts.invoice_CustomCheck(o.invoice)) return new Error(`${path}.invoice: custom check failed`)
if (typeof o.offer_id !== 'string') return new Error(`${path}.offer_id: is not a string`)
if (opts.offer_id_CustomCheck && !opts.offer_id_CustomCheck(o.offer_id)) return new Error(`${path}.offer_id: custom check failed`)
if (typeof o.paid_at_unix !== 'number') return new Error(`${path}.paid_at_unix: is not a number`)
if (opts.paid_at_unix_CustomCheck && !opts.paid_at_unix_CustomCheck(o.paid_at_unix)) return new Error(`${path}.paid_at_unix: custom check failed`)
return null
}
export type OfferInvoices = {
invoices: OfferInvoice[]
}
export const OfferInvoicesOptionalFields: [] = []
export type OfferInvoicesOptions = OptionsBaseMessage & {
checkOptionalsAreSet?: []
invoices_ItemOptions?: OfferInvoiceOptions
invoices_CustomCheck?: (v: OfferInvoice[]) => boolean
}
export const OfferInvoicesValidate = (o?: OfferInvoices, opts: OfferInvoicesOptions = {}, path: string = 'OfferInvoices::root.'): Error | null => {
if (opts.checkOptionalsAreSet && opts.allOptionalsAreSet) return new Error(path + ': only one of checkOptionalsAreSet or allOptionalNonDefault can be set for each message')
if (typeof o !== 'object' || o === null) return new Error(path + ': object is not an instance of an object or is null')
if (!Array.isArray(o.invoices)) return new Error(`${path}.invoices: is not an array`)
for (let index = 0; index < o.invoices.length; index++) {
const invoicesErr = OfferInvoiceValidate(o.invoices[index], opts.invoices_ItemOptions, `${path}.invoices[${index}]`)
if (invoicesErr !== null) return invoicesErr
}
if (opts.invoices_CustomCheck && !opts.invoices_CustomCheck(o.invoices)) return new Error(`${path}.invoices: custom check failed`)
return null
}
export type OpenChannel = { export type OpenChannel = {
active: boolean active: boolean
capacity: number capacity: number

View file

@ -481,6 +481,12 @@ service LightningPub {
option (http_route) = "/api/user/offer/get"; option (http_route) = "/api/user/offer/get";
option (nostr) = true; option (nostr) = true;
} }
rpc GetUserOfferInvoices(structs.GetUserOfferInvoicesReq) returns (structs.OfferInvoices){
option (auth_type) = "User";
option (http_method) = "post";
option (http_route) = "/api/user/offer/get/invoices";
option (nostr) = true;
}
rpc UpdateUserOffer(structs.OfferConfig) returns (structs.Empty){ rpc UpdateUserOffer(structs.OfferConfig) returns (structs.Empty){
option (auth_type) = "User"; option (auth_type) = "User";

View file

@ -642,3 +642,20 @@ message OfferConfig {
message UserOffers { message UserOffers {
repeated OfferConfig offers = 1; repeated OfferConfig offers = 1;
} }
message GetUserOfferInvoicesReq {
string offer_id = 1;
bool include_unpaid = 2;
}
message OfferInvoices {
repeated OfferInvoice invoices = 1;
}
message OfferInvoice {
string invoice = 1;
string offer_id = 2;
int64 paid_at_unix = 3;
int64 amount = 4;
map<string,string> data = 5;
}

View file

@ -44,11 +44,6 @@ const mapToOfferConfig = (appUserId: string, offer: UserOffer, { pubkey, relay }
export class OfferManager { export class OfferManager {
_nostrSend: NostrSend | null = null _nostrSend: NostrSend | null = null
applicationManager: ApplicationManager applicationManager: ApplicationManager
@ -97,6 +92,22 @@ export class OfferManager {
callback_url: req.callback_url, callback_url: req.callback_url,
}) })
} }
async GetUserOfferInvoices(ctx: Types.UserContext, req: Types.GetUserOfferInvoicesReq): Promise<Types.OfferInvoices> {
const userOffer = await this.storage.offerStorage.GetUserOffer(ctx.app_user_id, req.offer_id)
if (!userOffer) {
throw new Error("Offer not found")
}
const i = await this.storage.paymentStorage.GetOfferInvoices(req.offer_id, req.include_unpaid)
return {
invoices: i.map(i => ({
invoice: i.invoice,
offer_id: i.offer_id || "",
paid_at_unix: i.paid_at_unix,
amount: i.paid_amount,
data: i.payer_data || {}
}))
}
}
async GetUserOffer(ctx: Types.UserContext, req: Types.OfferId): Promise<Types.OfferConfig> { async GetUserOffer(ctx: Types.UserContext, req: Types.OfferId): Promise<Types.OfferConfig> {
const app = await this.applicationManager.GetApp(ctx.app_id) const app = await this.applicationManager.GetApp(ctx.app_id)

View file

@ -350,6 +350,13 @@ export default (mainHandler: Main): Types.ServerMethods => {
}) })
if (err != null) throw new Error(err.message) if (err != null) throw new Error(err.message)
return mainHandler.offerManager.GetUserOffer(ctx, req) return mainHandler.offerManager.GetUserOffer(ctx, req)
},
GetUserOfferInvoices: async ({ ctx, req }) => {
const err = Types.GetUserOfferInvoicesReqValidate(req, {
offer_id_CustomCheck: id => id !== ''
})
if (err != null) throw new Error(err.message)
return mainHandler.offerManager.GetUserOfferInvoices(ctx, req)
} }
} }
} }

View file

@ -454,4 +454,12 @@ export default class {
async GetPendingPayments(entityManager = this.DB) { async GetPendingPayments(entityManager = this.DB) {
return entityManager.getRepository(UserInvoicePayment).find({ where: { paid_at_unix: 0 } }) return entityManager.getRepository(UserInvoicePayment).find({ where: { paid_at_unix: 0 } })
} }
async GetOfferInvoices(offerId: string, includeUnpaid: boolean, entityManager = this.DB) {
const where: { offer_id: string, paid_at_unix?: FindOperator<number> } = { offer_id: offerId }
if (!includeUnpaid) {
where.paid_at_unix = MoreThan(0)
}
return entityManager.getRepository(UserReceivingInvoice).find({ where })
}
} }