Skip to content

Conversation

@asmogo
Copy link

@asmogo asmogo commented Nov 11, 2025

Fixes: #401

I have tested this with a CDK feature branch and the stripe payment processor

Description

added functions for minting and melting quotes with generic payment methods

Changes

  • new api functions for generic payment methods

PR Tasks

  • Open PR
  • run npm run test --> no failing unit tests
  • run npm run lint --> no warnings or errors
  • run npm run format
  • run npm run api:check --> run npm run api:update for changes to the API

@asmogo asmogo changed the title Feat/generic payment method feat: generic payment method Nov 11, 2025
Copy link
Collaborator

@Egge21M Egge21M left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you so much for experimenting with this! This is very similar to what I had in mind, however I think this might not be generic enough yet. Right now Wallet._mintProofsGeneric assumes the method to have the same config as what we see today (e.g. a key-pair). But payment methods in the future might have new parameters that are required by the mint. If we have a generic method we should make sure it is fully generic.

E.g.: The createMingQuoteGeneric method could not be used to generate a bolt12 quote, as it lacks a field for pubkey.

What if we replace all config parameters with factory functions or the config itself instead that are then called by cashu-ts to create things like MintQuotePayload?

E.g.:

	async createMintQuoteGeneric<T extends Record<string, unknown>>(
		method: string,
                payloadFactory: () => Record<string, unknown>
	): Promise<T & { quote: string; amount: number; unit: string; expiry: number }> {
		const payload = payloadFactory();
		return this.mint.createMintQuote<T>(method, payload);
	}

or in the case of mintProofsGeneric:

	 createMintQuoteGeneric<T extends Record<string, unknown>>(
		method: string,
                payloadFactory: (blindMessages) => Record<string, unknown>
	): Promise<T & { quote: string; amount: number; unit: string; expiry: number }>

const mintPayload: MintPayload = {
outputs: blindedMessages,
quote: typeof quote === 'string' ? quote : quote.quote,
quote: typeof quote === 'string' ? quote : (quote.quote as string),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is quote.quote asserted to be a string here if the params define it as unkown?

@robwoodgate
Copy link
Collaborator

robwoodgate commented Nov 11, 2025

This is a solid start, thank you for this! As Egge says, I also wonder if it can be more generic.

I was thinking something along the lines of:

// Mint class method
public async createMintQuote<
  TRes extends Record<string, unknown> = Record<string, unknown>
>(
  method: string,
  mintQuotePayload: MintQuoteBaseRequest & Record<string, unknown>,
  customRequest?: RequestFn
): Promise<MintQuoteBaseResponse & TRes> {
  // Validate method string and make request
  failIf(!this.isValidMethodString(method), `Invalid mint quote method: ${method}`, this._logger);
  return this.requestWithAuth<MintQuoteBaseResponse & TRes>(
    'POST',
    `/v1/mint/quote/${method}`,
    { requestBody: mintQuotePayload },
    customRequest
  );
}

// Example: custom bank transfer (as below, or  MintQuoteBaseRequest & { amount: number; } )
type BankTransferQuoteReq = {
  unit: string;
  amount: number;
};

type BankTransferQuoteRes = {
  expiry: number;     // unix seconds until which the instructions are valid
  reference: string;  // payer reference to include on the transfer
};

const bankQuote = await mint.createMintQuote<BankTransferQuoteRes>(
  'bank',
  { unit: 'sat', amount: 25000 }
);

// bankQuote is typed as:
/// {
///   quote: string;
///   request: string;
///   unit: string;
///   expiry: number;
///   reference: string;
/// }

// Example response shape:
/// {
///   "quote": "q_9b7f2c...",
///   "request": "Beneficiary: Cashu Mint Ltd; IBAN: DE89 3704 0044 0532 0130 00; BIC: COLSDE33; Amount: 25000 sat; Reference: REF-2Y6Q9N",
///   "unit": "sat",
///   "expiry": 1731369600,
///   "reference": "REF-2Y6Q9N"
/// }

@robwoodgate
Copy link
Collaborator

GM @asmogo - You'll be pleased to know that the SIG_ALL changes and type refactor PRs are now merged, so you should have a clear run at this now.

As part of SIG_ALL, I implemented a generic melt:
Mint side: https://github.com/cashubtc/cashu-ts/blob/development/src/mint/Mint.ts#L417
Wallet side: https://github.com/cashubtc/cashu-ts/blob/development/src/wallet/Wallet.ts#L1961

The Mint/Melt types have also been refactored to make them easier to use with generics:
NUT-04: https://github.com/cashubtc/cashu-ts/blob/development/src/model/types/NUT04.ts
NUT-05:https://github.com/cashubtc/cashu-ts/blob/development/src/model/types/NUT05.ts

@robwoodgate robwoodgate added the needs rebase This PR needs to be rebased to development because it contains merge conflicts label Dec 9, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

needs rebase This PR needs to be rebased to development because it contains merge conflicts

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Make Mint / Melt generic per NUT-04/05

3 participants