Skip to content

Commit

Permalink
Merge branch 'develop' into IN-155-hide-payments-pages
Browse files Browse the repository at this point in the history
  • Loading branch information
Oleksandr Raspopov committed Jan 27, 2025
2 parents 4ac4a77 + c4a73bc commit 8bdc988
Show file tree
Hide file tree
Showing 34 changed files with 555 additions and 290 deletions.
6 changes: 4 additions & 2 deletions .env-issuer.sample
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,9 @@ ISSUER_RESOLVER_FILE=
ISSUER_UNIVERSAL_LINKS_BASE_URL=https://wallet.privado.id

#Payments configuration
# ISSUER_PAYMENTS_SETTINGS_PATH is the configutation file for payments.
# ISSUER_PAYMENTS_SETTINGS_PATH is the configuration file for payments.
# You can use another file by specifying the path. Be Sure to the file is mounted in the container (docker compose files)
ISSUER_PAYMENTS_SETTINGS_PATH=./payment_settings.yaml
#

#if you want, you can specify the content of the payments configuration encoded in base64. In this case ISSUER_PAYMENTS_SETTINGS_PATH have to be empty
ISSUER_PAYMENTS_SETTINGS_FILE=
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ bin/
.env-issuer
.env-ui
resolvers_settings.yaml
payment_settings.yaml

infrastructure/local/.vault/data
infrastructure/local/.vault/plugins
Expand Down
5 changes: 0 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,15 +98,12 @@ bd7b69984f1c privadoid/issuernode-api "sh -c './migrate &&…" 38 sec
a4a1d3ec9159 redis:6-alpine "docker-entrypoint.s…" 38 seconds ago Up 36 seconds (healthy) 6379/tcp issuer-redis-1
```


5. Import your Ethereum private key

```shell
make private_key=<private-key> import-private-key-to-kms
```

**_TODO_**: Add section to configure payments

then visit:
* http://localhost:8088/ to access the UI (default username / password are: user-ui, password-ui). You can set them using env [vars](.env-ui.sample).
* <PUBLICLY_ACCESSIBLE_URL_POINTING_TO_ISSUER_SERVER_PORT>:3001/ to access the API. (default username / password are: user-issuer, password-issuer)
Expand Down Expand Up @@ -144,7 +141,6 @@ then modify the file with the proper values. The most important fields to run th
In this file you can define customizations for each type of blockchain and network. For this example, we only need to
define the RPCs. that will use.

**_TODO_**: Add section to configure payments****

4. Copy .env-ui sample file and fill the needed env variables:

Expand Down Expand Up @@ -176,7 +172,6 @@ action the given account has to be funded. For Amoy network you can request some
make private_key=<private-key> import-private-key-to-kms
```


### Running only Issuer Node API (docker compose and build from source)

If you want to run only the API, you can follow the steps below. You have to have the .env-issuer file filled with
Expand Down
6 changes: 5 additions & 1 deletion cmd/platform/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,11 @@ func main() {
displayMethodService := services.NewDisplayMethod(repositories.NewDisplayMethod(*storage))
schemaService := services.NewSchema(schemaRepository, schemaLoader, displayMethodService)
linkService := services.NewLinkService(storage, claimsService, qrService, claimsRepository, linkRepository, schemaRepository, schemaLoader, sessionRepository, ps, identityService, *networkResolver, cfg.UniversalLinks)
paymentService := services.NewPaymentService(paymentsRepo, *networkResolver, schemaService, paymentSettings, keyStore)
paymentService, err := services.NewPaymentService(paymentsRepo, *networkResolver, schemaService, paymentSettings, keyStore)
if err != nil {
log.Error(ctx, "error creating payment service", "err", err)
return
}
keyService := services.NewKey(keyStore, claimsService, keyRepository)
transactionService, err := gateways.NewTransaction(*networkResolver)
if err != nil {
Expand Down
4 changes: 2 additions & 2 deletions internal/api/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -353,8 +353,8 @@ func newTestServer(t *testing.T, st *db.Storage) *testServer {
connectionService := services.NewConnection(repos.connection, repos.claims, st)
displayMethodService := services.NewDisplayMethod(repos.displayMethod)
schemaService := services.NewSchema(repos.schemas, schemaLoader, displayMethodService)
paymentService := services.NewPaymentService(repos.payments, *networkResolver, schemaService, paymentSettings, keyStore)

paymentService, err := services.NewPaymentService(repos.payments, *networkResolver, schemaService, paymentSettings, keyStore)
require.NoError(t, err)
mediaTypeManager := services.NewMediaTypeManager(
map[iden3comm.ProtocolMessage][]string{
protocol.CredentialFetchRequestMessageType: {string(packers.MediaTypeZKPMessage)},
Expand Down
3 changes: 3 additions & 0 deletions internal/core/domain/schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ const (

AuthBJJCredentialJSONLDContext = "https://schema.iden3.io/core/jsonld/auth.jsonld"
AuthBJJCredentialTypeID = "https://schema.iden3.io/core/jsonld/auth.jsonld#AuthBJJCredential"

Iden3PaymentRailsRequestV1SchemaJSON = `{"EIP712Domain":[{"name":"name","type":"string"},{"name":"version","type":"string"},{"name":"chainId","type":"uint256"},{"name":"verifyingContract","type":"address"}],"Iden3PaymentRailsRequestV1":[{"name":"recipient","type":"address"},{"name":"amount","type":"uint256"},{"name":"expirationDate","type":"uint256"},{"name":"nonce","type":"uint256"},{"name":"metadata","type":"bytes"}]}`
Iden3PaymentRailsERC20RequestV1SchemaJSON = `{"EIP712Domain":[{"name":"name","type":"string"},{"name":"version","type":"string"},{"name":"chainId","type":"uint256"},{"name":"verifyingContract","type":"address"}],"Iden3PaymentRailsERC20RequestV1":[{"name":"tokenAddress","type":"address"},{"name":"recipient","type":"address"},{"name":"amount","type":"uint256"},{"name":"expirationDate","type":"uint256"},{"name":"nonce","type":"uint256"},{"name":"metadata","type":"bytes"}]}`
)

// SchemaFormat type
Expand Down
109 changes: 41 additions & 68 deletions internal/core/services/payment.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"crypto/rand"
b64 "encoding/base64"
"encoding/hex"
"encoding/json"
"fmt"
"math/big"
"strconv"
Expand Down Expand Up @@ -33,22 +34,38 @@ import (
)

type payment struct {
networkResolver network.Resolver
settings payments.Config
schemaService ports.SchemaService
paymentsStore ports.PaymentRepository
kms kms.KMSType
networkResolver network.Resolver
settings payments.Config
schemaService ports.SchemaService
paymentsStore ports.PaymentRepository
kms kms.KMSType
iden3PaymentRailsRequestV1Types apitypes.Types
iden3PaymentRailsERC20RequestV1Types apitypes.Types
}

// NewPaymentService creates a new payment service
func NewPaymentService(payOptsRepo ports.PaymentRepository, resolver network.Resolver, schemaSrv ports.SchemaService, settings *payments.Config, kms kms.KMSType) ports.PaymentService {
return &payment{
networkResolver: resolver,
settings: *settings,
schemaService: schemaSrv,
paymentsStore: payOptsRepo,
kms: kms,
func NewPaymentService(payOptsRepo ports.PaymentRepository, resolver network.Resolver, schemaSrv ports.SchemaService, settings *payments.Config, kms kms.KMSType) (ports.PaymentService, error) {
iden3PaymentRailsRequestV1Types := apitypes.Types{}
iden3PaymentRailsERC20RequestV1Types := apitypes.Types{}
err := json.Unmarshal([]byte(domain.Iden3PaymentRailsRequestV1SchemaJSON), &iden3PaymentRailsRequestV1Types)
if err != nil {
log.Error(context.Background(), "failed to unmarshal Iden3PaymentRailsRequestV1 schema", "err", err)
return nil, err
}
err = json.Unmarshal([]byte(domain.Iden3PaymentRailsERC20RequestV1SchemaJSON), &iden3PaymentRailsERC20RequestV1Types)
if err != nil {
log.Error(context.Background(), "failed to unmarshal Iden3PaymentRailsERC20RequestV1 schema", "err", err)
return nil, err
}
return &payment{
networkResolver: resolver,
settings: *settings,
schemaService: schemaSrv,
paymentsStore: payOptsRepo,
kms: kms,
iden3PaymentRailsRequestV1Types: iden3PaymentRailsRequestV1Types,
iden3PaymentRailsERC20RequestV1Types: iden3PaymentRailsERC20RequestV1Types,
}, nil
}

// CreatePaymentOption creates a payment option for a specific issuer
Expand Down Expand Up @@ -440,37 +457,19 @@ func (p *payment) paymentRequestSignature(
ID: string(decodedKeyID),
}

var types apitypes.Types
switch paymentType {
case string(protocol.Iden3PaymentRailsRequestV1Type):
types = p.iden3PaymentRailsRequestV1Types
case string(protocol.Iden3PaymentRailsERC20RequestV1Type):
types = p.iden3PaymentRailsERC20RequestV1Types
default:
log.Error(ctx, fmt.Sprintf("unsupported payment type '%s'", paymentType), "err", err)
return nil, fmt.Errorf("unsupported payment type '%s:'", paymentType)
}

typedData := apitypes.TypedData{
Types: apitypes.Types{
"EIP712Domain": []apitypes.Type{
{Name: "name", Type: "string"},
{Name: "version", Type: "string"},
{Name: "chainId", Type: "uint256"},
{Name: "verifyingContract", Type: "address"},
},
paymentType: []apitypes.Type{
{
Name: "recipient",
Type: "address",
},
{
Name: "amount",
Type: "uint256",
},
{
Name: "expirationDate",
Type: "uint256",
},
{
Name: "nonce",
Type: "uint256",
},
{
Name: "metadata",
Type: "bytes",
},
},
},
Types: types,
PrimaryType: paymentType,
Domain: apitypes.TypedDataDomain{
Name: "MCPayment",
Expand All @@ -487,32 +486,6 @@ func (p *payment) paymentRequestSignature(
},
}
if paymentType == string(protocol.Iden3PaymentRailsERC20RequestV1Type) {
typedData.Types[paymentType] = []apitypes.Type{
{
Name: "tokenAddress",
Type: "address",
},
{
Name: "recipient",
Type: "address",
},
{
Name: "amount",
Type: "uint256",
},
{
Name: "expirationDate",
Type: "uint256",
},
{
Name: "nonce",
Type: "uint256",
},
{
Name: "metadata",
Type: "bytes",
},
}
typedData.Message["tokenAddress"] = setting.PaymentOption.ContractAddress.String()
}
typedDataBytes, _, err := apitypes.TypedDataAndHash(typedData)
Expand Down
76 changes: 55 additions & 21 deletions internal/repositories/payment.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,12 @@ import (
"github.com/google/uuid"
"github.com/iden3/go-iden3-core/v2/w3c"
"github.com/iden3/iden3comm/v2/protocol"
"github.com/jackc/pgtype"

"github.com/polygonid/sh-id-platform/internal/core/domain"
"github.com/polygonid/sh-id-platform/internal/core/ports"
"github.com/polygonid/sh-id-platform/internal/db"
"github.com/polygonid/sh-id-platform/internal/payments"
)

// ErrPaymentOptionDoesNotExists error
Expand Down Expand Up @@ -181,10 +183,31 @@ func (p *payment) DeletePaymentRequest(ctx context.Context, issuerDID w3c.DID, i
// GetAllPaymentRequests returns all payment requests
func (p *payment) GetAllPaymentRequests(ctx context.Context, issuerDID w3c.DID) ([]domain.PaymentRequest, error) {
const query = `
SELECT pr.id, pr.description, pr.credentials, pr.issuer_did, pr.recipient_did, pr.payment_option_id, pr.created_at, pri.id, pri.nonce, pri.payment_request_id, pri.payment_request_info, pri.payment_option_id, pri.signing_key
SELECT pr.id,
pr.description,
pr.credentials,
pr.issuer_did,
pr.recipient_did,
pr.payment_option_id,
pr.created_at,
COALESCE(
JSON_AGG(
JSON_BUILD_OBJECT(
'id', pri.id,
'nc', pri.nonce::text,
'rid', pri.payment_request_id,
'rnfo', pri.payment_request_info,
'optid', pri.payment_option_id,
'sk', pri.signing_key
)
) FILTER (WHERE pri.id IS NOT NULL),
'[]'
) AS payment_request_items
FROM payment_requests pr
LEFT JOIN payment_request_items pri ON pr.id = pri.payment_request_id
WHERE pr.issuer_did = $1`
WHERE pr.issuer_did = $1
GROUP BY pr.id, pr.description, pr.credentials, pr.issuer_did, pr.recipient_did, pr.payment_option_id, pr.created_at
`
rows, err := p.conn.Pgx.Query(ctx, query, issuerDID.String())
if err != nil {
return nil, err
Expand All @@ -193,12 +216,10 @@ WHERE pr.issuer_did = $1`
var pr domain.PaymentRequest
var requests []domain.PaymentRequest
for rows.Next() {
var item domain.PaymentRequestItem
var strIssuerDID, strUserDID string
var sNonce string
var did *w3c.DID
var paymentRequestInfoBytes []byte
var paymentCredentials []byte
var requestItems pgtype.JSON
if err := rows.Scan(
&pr.ID,
&pr.Description,
Expand All @@ -207,27 +228,41 @@ WHERE pr.issuer_did = $1`
&strUserDID,
&pr.PaymentOptionID,
&pr.CreatedAt,
&item.ID,
&sNonce,
&item.PaymentRequestID,
&paymentRequestInfoBytes,
&item.PaymentOptionID,
&item.SigningKeyID,
&requestItems,
); err != nil {
return nil, fmt.Errorf("could not scan payment request: %w", err)
}

item.Payment, err = p.paymentRequestItem(paymentRequestInfoBytes)
if err != nil {
return nil, fmt.Errorf("could not unmarshal payment request info: %w", err)
var itemDtoCol []struct {
ID string `json:"id"`
Nonce string `json:"nc"`
PaymentRequestID string `json:"rid"`
PaymentRequestInfo protocol.PaymentRequestInfoData `json:"rnfo"`
PaymentOptionID int `json:"optid"`
SigningKey string `json:"sk"`
}
if err := requestItems.AssignTo(&itemDtoCol); err != nil {
return nil, fmt.Errorf("could not assign to payment request items: %w", err)
}
pr.Payments = make([]domain.PaymentRequestItem, len(itemDtoCol))
for i, itemDto := range itemDtoCol {
pr.Payments[i].ID, err = uuid.Parse(itemDto.ID)
if err != nil {
return nil, fmt.Errorf("could not parse payment request item ID: %w", err)
}
pr.Payments[i].PaymentRequestID, err = uuid.Parse(itemDto.PaymentRequestID)
if err != nil {
return nil, fmt.Errorf("could not parse payment request ID: %w", err)
}
nonce, ok := new(big.Int).SetString(itemDto.Nonce, 10) //nolint:mnd
if !ok {
return nil, fmt.Errorf("could not parse nonce into big.Int: %s", itemDto.Nonce)
}
pr.Payments[i].Nonce = *nonce
pr.Payments[i].PaymentOptionID = payments.OptionConfigIDType(itemDto.PaymentOptionID)
pr.Payments[i].SigningKeyID = itemDto.SigningKey
pr.Payments[i].Payment = itemDto.PaymentRequestInfo[0]

const base10 = 10
nonce, ok := new(big.Int).SetString(sNonce, base10)
if !ok {
return nil, fmt.Errorf("could not parse nonce: %w", err)
}
item.Nonce = *nonce
if did, err = w3c.ParseDID(strIssuerDID); err != nil {
return nil, fmt.Errorf("could not parse issuer DID: %w", err)
}
Expand All @@ -240,7 +275,6 @@ WHERE pr.issuer_did = $1`
if err != nil {
return nil, fmt.Errorf("could not unmarshal payment credentials info: %w", err)
}
pr.Payments = append(pr.Payments, item)

requests = append(requests, pr)
}
Expand Down
7 changes: 5 additions & 2 deletions internal/repositories/payment_request_fixture.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ package repositories

import (
"context"
"crypto/rand"
"math/big"
"math/rand"
"testing"
"time"

Expand All @@ -24,7 +24,10 @@ func (f *Fixture) CreatePaymentRequest(t *testing.T, issuerDID, userDID w3c.DID,
var paymentList []domain.PaymentRequestItem
paymentRequestID := uuid.New()
for i := 0; i < nPayments; i++ {
nonce := big.NewInt(rand.Int63())
nonceBytes := make([]byte, 16) // nolint:mnd
_, err := rand.Read(nonceBytes)
require.NoError(t, err)
nonce := new(big.Int).SetBytes(nonceBytes)
paymentList = append(paymentList, domain.PaymentRequestItem{
ID: uuid.New(),
Nonce: *nonce,
Expand Down
Loading

0 comments on commit 8bdc988

Please sign in to comment.