Created
August 31, 2021 05:47
-
-
Save johndpope/dd8f1a47cfe3b6b6b2b264c7c2f8eedd to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package app | |
import ( | |
"context" | |
"fin-go/routes/accounts" | |
"fin-go/routes/analysisTrees" | |
"fin-go/routes/categories" | |
"fin-go/routes/itemTokens" | |
"fin-go/routes/plaidHelper" | |
"fin-go/routes/resetDB" | |
"fin-go/routes/saltedge" | |
"fin-go/routes/transactions" | |
"net/http" | |
"github.com/gorilla/mux" | |
"github.com/plaid/plaid-go/plaid" | |
"github.com/gin-gonic/gin" | |
) | |
type App struct { | |
Router *mux.Router | |
} | |
func (app *App) SetupRouter() { | |
app.Router. | |
Methods("GET"). | |
Path("/api/set_access_token"). | |
HandlerFunc(plaidHelper.GetAccessToken()) | |
app.Router. | |
Methods("POST"). | |
Path("/api/create_link_token_for_payment"). | |
HandlerFunc(plaidHelper.CreateLinkTokenForPayment()) | |
app.Router. | |
Methods("GET"). | |
Path("/api/auth"). | |
HandlerFunc(plaidHelper.Auth()) | |
// app.Router. | |
// Methods("GET"). | |
// Path("/api/accounts"). | |
// HandlerFunc(plaidHelper.Accounts()) | |
app.Router. | |
Methods("GET"). | |
Path("/api/balance"). | |
HandlerFunc(plaidHelper.Balance()) | |
app.Router. | |
Methods("GET"). | |
Path("/api/item"). | |
HandlerFunc(plaidHelper.Item()) | |
app.Router. | |
Methods("GET"). | |
Path("/api/identity"). | |
HandlerFunc(plaidHelper.Identity()) | |
app.Router. | |
Methods("GET"). | |
Path("/api/transactions"). | |
HandlerFunc(plaidHelper.Transactions()) | |
// TODO review this | |
app.Router. | |
Methods("POST"). | |
Path("/api/transactions"). | |
HandlerFunc(plaidHelper.Transactions()) | |
// app.Router. | |
// Methods("POST"). | |
// Path("/api/plaidGeneratePublicToken"). | |
// HandlerFunc(plaid.GeneratePublicTokenFunction()) | |
app.Router. | |
Methods("GET"). | |
Path("/api/payment"). | |
HandlerFunc(plaidHelper.Payment()) | |
app.Router. | |
Methods("GET"). | |
Path("/api/create_public_token"). | |
HandlerFunc(plaidHelper.CreatePublicToken()) | |
app.Router. | |
Methods("GET"). | |
Path("/api/create_link_token"). | |
HandlerFunc(plaidHelper.CreateLinkToken()) | |
app.Router. | |
Methods("GET"). | |
Path("/api/investment_transactions"). | |
HandlerFunc(plaidHelper.InvestmentTransactions()) | |
app.Router. | |
Methods("GET"). | |
Path("/api/holdings"). | |
HandlerFunc(plaidHelper.Holdings()) | |
app.Router. | |
Methods("GET"). | |
Path("/api/assets"). | |
HandlerFunc(plaidHelper.Assets()) | |
// Use the Database here - | |
app.Router. | |
Methods("GET"). | |
Path("/api/accounts"). | |
HandlerFunc(accounts.GetFunction()) | |
app.Router. | |
Methods("POST"). | |
Path("/api/accountUpsertName"). | |
HandlerFunc(accounts.UpsertNameFunction()) | |
app.Router. | |
Methods("POST"). | |
Path("/api/accountUpsertIgnore"). | |
HandlerFunc(accounts.UpsertIgnoreFunction()) | |
app.Router. | |
Methods("POST"). | |
Path("/api/transactionUpsert"). | |
HandlerFunc(transactions.UpsertFunction()) | |
app.Router. | |
Methods("GET"). | |
Path("/api/categories"). | |
HandlerFunc(categories.GetFunction()) | |
app.Router. | |
Methods("GET"). | |
Path("/api/itemTokens"). | |
HandlerFunc(itemTokens.GetFunction()) | |
app.Router. | |
Methods("GET"). | |
Path("/api/itemTokensFetchTransactions"). | |
HandlerFunc(itemTokens.FetchTransactionsFunction()) | |
app.Router. | |
Methods("GET"). | |
Path("/api/transactions"). | |
HandlerFunc(transactions.GetFunction()) | |
app.Router. | |
Methods("PUT"). | |
Path("/api/transactions"). | |
HandlerFunc(transactions.PutFunction()) | |
//Step one of import | |
app.Router. | |
Methods("POST"). | |
Path("/api/checkTransactions"). | |
HandlerFunc(transactions.CheckFunction()) | |
//Step two of import | |
app.Router. | |
Methods("POST"). | |
Path("/api/importTransactions"). | |
HandlerFunc(transactions.ImportFunction()) | |
app.Router. | |
Methods("GET"). | |
Path("/api/analysisTrees"). | |
HandlerFunc(analysisTrees.GetFunction()) | |
app.Router. | |
Methods("GET"). | |
Path("/api/saltEdgeRefreshInteractive/{id}"). | |
HandlerFunc(saltedge.RefreshConnectionInteractiveFunction()) | |
app.Router. | |
Methods("GET"). | |
Path("/api/saltEdgeCreateInteractive"). | |
HandlerFunc(saltedge.CreateConnectionInteractiveFunction()) | |
app.Router. | |
Methods("GET"). | |
Path("/api/resetDB"). | |
HandlerFunc(resetDB.ForceResetDBFunction()) | |
app.Router. | |
Methods("GET"). | |
Path("/api/resetDBFull"). | |
HandlerFunc(resetDB.ForceResetDBFullFunction()) | |
app.Router. | |
Methods("POST"). | |
Path("/api/customTree"). | |
HandlerFunc(analysisTrees.CustomAnalyze()) | |
} | |
func createPublicToken(c *gin.Context) { | |
ctx := context.Background() | |
// Create a one-time use public_token for the Item. | |
// This public_token can be used to initialize Link in update mode for a user | |
publicTokenCreateResp, _, err := client.PlaidApi.ItemCreatePublicToken(ctx).ItemPublicTokenCreateRequest( | |
*plaid.NewItemPublicTokenCreateRequest(accessToken), | |
).Execute() | |
if err != nil { | |
renderError(c, err) | |
return | |
} | |
c.JSON(http.StatusOK, gin.H{ | |
"public_token": publicTokenCreateResp.GetPublicToken(), | |
}) | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package plaidHelper | |
import ( | |
"bufio" | |
"context" | |
"encoding/base64" | |
"errors" | |
"fmt" | |
"io/ioutil" | |
"log" | |
"net/http" | |
"os" | |
"strings" | |
"time" | |
"encoding/json" | |
"github.com/joho/godotenv" | |
"github.com/plaid/plaid-go/plaid" | |
) | |
var ( | |
PLAID_CLIENT_ID = "" | |
PLAID_SECRET = "" | |
PLAID_ENV = "" | |
PLAID_PRODUCTS = "" | |
PLAID_COUNTRY_CODES = "" | |
PLAID_REDIRECT_URI = "" | |
APP_PORT = "" | |
client *plaid.APIClient = nil | |
) | |
var environments = map[string]plaid.Environment{ | |
"sandbox": plaid.Sandbox, | |
"development": plaid.Development, | |
"production": plaid.Production, | |
} | |
func init() { | |
// load env vars from .env file | |
err := godotenv.Load() | |
if err != nil { | |
fmt.Println("Error when loading environment variables from .env file %w", err) | |
} | |
// set constants from env | |
PLAID_CLIENT_ID = os.Getenv("PLAID_CLIENT_ID") | |
PLAID_SECRET = os.Getenv("PLAID_SECRET") | |
if PLAID_CLIENT_ID == "" || PLAID_SECRET == "" { | |
log.Fatal("Error: PLAID_SECRET or PLAID_CLIENT_ID is not set. Did you copy .env.example to .env and fill it out?") | |
} | |
PLAID_ENV = os.Getenv("PLAID_ENV") | |
PLAID_PRODUCTS = os.Getenv("PLAID_PRODUCTS") | |
PLAID_COUNTRY_CODES = os.Getenv("PLAID_COUNTRY_CODES") | |
PLAID_REDIRECT_URI = os.Getenv("PLAID_REDIRECT_URI") | |
APP_PORT = os.Getenv("APP_PORT") | |
// set defaults | |
if PLAID_PRODUCTS == "" { | |
PLAID_PRODUCTS = "transactions" | |
} | |
if PLAID_COUNTRY_CODES == "" { | |
PLAID_COUNTRY_CODES = "US" | |
} | |
if PLAID_ENV == "" { | |
PLAID_ENV = "sandbox" | |
} | |
if APP_PORT == "" { | |
APP_PORT = "8000" | |
} | |
if PLAID_CLIENT_ID == "" { | |
log.Fatal("PLAID_CLIENT_ID is not set. Make sure to fill out the .env file") | |
} | |
if PLAID_SECRET == "" { | |
log.Fatal("PLAID_SECRET is not set. Make sure to fill out the .env file") | |
} | |
// create Plaid client | |
configuration := plaid.NewConfiguration() | |
configuration.AddDefaultHeader("PLAID-CLIENT-ID", PLAID_CLIENT_ID) | |
configuration.AddDefaultHeader("PLAID-SECRET", PLAID_SECRET) | |
configuration.UseEnvironment(environments[PLAID_ENV]) | |
client = plaid.NewAPIClient(configuration) | |
} | |
/* | |
func main() { | |
r := gin.Default() | |
r.POST("/api/info", info) | |
// For OAuth flows, the process looks as follows. | |
// 1. Create a link token with the redirectURI (as white listed at https://dashboard.plaid.com/team/api). | |
// 2. Once the flow succeeds, Plaid Link will redirect to redirectURI with | |
// additional parameters (as required by OAuth standards and Plaid). | |
// 3. Re-initialize with the link token (from step 1) and the full received redirect URI | |
// from step 2. | |
r.POST("/api/set_access_token", getAccessToken) | |
r.POST("/api/create_link_token_for_payment", createLinkTokenForPayment) | |
r.GET("/api/auth", auth) | |
r.GET("/api/accounts", accounts) | |
r.GET("/api/balance", balance) | |
r.GET("/api/item", item) | |
r.POST("/api/item", item) | |
r.GET("/api/identity", identity) | |
r.GET("/api/transactions", transactions) | |
r.POST("/api/transactions", transactions) | |
r.GET("/api/payment", payment) | |
r.GET("/api/create_public_token", createPublicToken) | |
r.POST("/api/create_link_token", createLinkToken) | |
r.GET("/api/investment_transactions", investmentTransactions) | |
r.GET("/api/holdings", holdings) | |
r.GET("/api/assets", assets) | |
err := r.Run(":" + APP_PORT) | |
if err != nil { | |
renderError("unable to start server") | |
} | |
}*/ | |
// We store the access_token in memory - in production, store it in a secure | |
// persistent data store. | |
var accessToken string | |
var itemID string | |
var paymentID string | |
func renderError(originalErr error) { | |
/*if plaidError, err := plaid.ToPlaidError(originalErr); err == nil { | |
// Return 200 and allow the front end to render the error. | |
c.JSON(http.StatusOK, gin.H{"error": plaidError}) | |
return | |
} | |
c.JSON(http.StatusInternalServerError, gin.H{"error": originalErr.Error()})*/ | |
} | |
func GetAccessToken() func(w http.ResponseWriter, r *http.Request) { | |
return func(res http.ResponseWriter, req *http.Request) { | |
ctx := req.Context() | |
publicToken := req.FormValue("public_token") | |
// exchange the public_token for an access_token | |
exchangePublicTokenResp, _, err := client.PlaidApi.ItemPublicTokenExchange(ctx).ItemPublicTokenExchangeRequest( | |
*plaid.NewItemPublicTokenExchangeRequest(publicToken), | |
).Execute() | |
if err != nil { | |
renderError(err) | |
return | |
} | |
accessToken = exchangePublicTokenResp.GetAccessToken() | |
itemID = exchangePublicTokenResp.GetItemId() | |
fmt.Println("public token: " + publicToken) | |
fmt.Println("access token: " + accessToken) | |
fmt.Println("item ID: " + itemID) | |
res.WriteHeader(http.StatusOK) | |
data := make(map[string]string) | |
data["access_token"] = accessToken | |
data["item_id"] = itemID | |
res.WriteHeader(http.StatusOK) | |
if err := json.NewEncoder(res).Encode(data); err != nil { | |
renderError(err) | |
} | |
} | |
} | |
// This functionality is only relevant for the UK Payment Initiation product. | |
// Creates a link token configured for payment initiation. The payment | |
// information will be associated with the link token, and will not have to be | |
// passed in again when we initialize Plaid Link. | |
func CreateLinkTokenForPayment() func(w http.ResponseWriter, r *http.Request) { | |
return func(res http.ResponseWriter, req *http.Request) { | |
ctx := req.Context() | |
// Create payment recipient | |
paymentRecipientRequest := plaid.NewPaymentInitiationRecipientCreateRequest("Harry Potter") | |
paymentRecipientRequest.SetIban("GB33BUKB20201555555555") | |
paymentRecipientRequest.SetAddress(*plaid.NewPaymentInitiationAddress( | |
[]string{"4 Privet Drive"}, | |
"Little Whinging", | |
"11111", | |
"GB", | |
)) | |
paymentRecipientCreateResp, _, err := client.PlaidApi.PaymentInitiationRecipientCreate(ctx).PaymentInitiationRecipientCreateRequest(*paymentRecipientRequest).Execute() | |
if err != nil { | |
renderError(err) | |
return | |
} | |
// Create payment | |
paymentCreateRequest := plaid.NewPaymentInitiationPaymentCreateRequest( | |
paymentRecipientCreateResp.GetRecipientId(), | |
"paymentRef", | |
*plaid.NewPaymentAmount("GBP", 12.34), | |
) | |
paymentCreateResp, _, err := client.PlaidApi.PaymentInitiationPaymentCreate(ctx).PaymentInitiationPaymentCreateRequest(*paymentCreateRequest).Execute() | |
if err != nil { | |
renderError(err) | |
return | |
} | |
paymentID = paymentCreateResp.GetPaymentId() | |
fmt.Println("payment id: " + paymentID) | |
linkTokenCreateReqPaymentInitiation := plaid.NewLinkTokenCreateRequestPaymentInitiation(paymentID) | |
linkToken, err := linkTokenCreate(ctx, linkTokenCreateReqPaymentInitiation) | |
data := make(map[string]string) | |
data["link_token"] = linkToken | |
res.WriteHeader(http.StatusOK) | |
if err := json.NewEncoder(res).Encode(data); err != nil { | |
renderError(err) | |
} | |
} | |
} | |
func Auth() func(w http.ResponseWriter, r *http.Request) { | |
return func(res http.ResponseWriter, req *http.Request) { | |
ctx := req.Context() | |
authGetResp, _, err := client.PlaidApi.AuthGet(ctx).AuthGetRequest( | |
*plaid.NewAuthGetRequest(accessToken), | |
).Execute() | |
if err != nil { | |
renderError(err) | |
return | |
} | |
var data map[string]interface{} | |
data["accounts"] = authGetResp.GetAccounts() | |
data["numbers"] = authGetResp.GetNumbers() | |
res.WriteHeader(http.StatusOK) | |
if err := json.NewEncoder(res).Encode(data); err != nil { | |
renderError(err) | |
} | |
} | |
} | |
func Accounts() func(w http.ResponseWriter, r *http.Request) { | |
return func(res http.ResponseWriter, req *http.Request) { | |
ctx := req.Context() | |
accountsGetResp, _, err := client.PlaidApi.AccountsGet(ctx).AccountsGetRequest( | |
*plaid.NewAccountsGetRequest(accessToken), | |
).Execute() | |
if err != nil { | |
renderError(err) | |
return | |
} | |
res.WriteHeader(http.StatusOK) | |
var data map[string]interface{} | |
data["accounts"] = accountsGetResp.GetAccounts() | |
if err := json.NewEncoder(res).Encode(data); err != nil { | |
renderError(err) | |
} | |
} | |
} | |
func Balance() func(w http.ResponseWriter, r *http.Request) { | |
return func(res http.ResponseWriter, req *http.Request) { | |
ctx := req.Context() | |
balancesGetResp, _, err := client.PlaidApi.AccountsBalanceGet(ctx).AccountsBalanceGetRequest( | |
*plaid.NewAccountsBalanceGetRequest(accessToken), | |
).Execute() | |
if err != nil { | |
renderError(err) | |
return | |
} | |
res.WriteHeader(http.StatusOK) | |
var data map[string]interface{} | |
data["accounts"] = balancesGetResp.GetAccounts() | |
if err := json.NewEncoder(res).Encode(data); err != nil { | |
renderError(err) | |
} | |
} | |
} | |
func Item() func(w http.ResponseWriter, r *http.Request) { | |
return func(res http.ResponseWriter, req *http.Request) { | |
ctx := req.Context() | |
itemGetResp, _, err := client.PlaidApi.ItemGet(ctx).ItemGetRequest( | |
*plaid.NewItemGetRequest(accessToken), | |
).Execute() | |
if err != nil { | |
renderError(err) | |
return | |
} | |
institutionGetByIdResp, _, err := client.PlaidApi.InstitutionsGetById(ctx).InstitutionsGetByIdRequest( | |
*plaid.NewInstitutionsGetByIdRequest( | |
*itemGetResp.GetItem().InstitutionId.Get(), | |
convertCountryCodes(strings.Split(PLAID_COUNTRY_CODES, ",")), | |
), | |
).Execute() | |
var data map[string]interface{} | |
data["item"] = itemGetResp.GetItem() | |
data["institution"] = institutionGetByIdResp.GetInstitution() | |
res.WriteHeader(http.StatusOK) | |
if err := json.NewEncoder(res).Encode(data); err != nil { | |
renderError(err) | |
} | |
} | |
} | |
func Identity() func(w http.ResponseWriter, r *http.Request) { | |
return func(res http.ResponseWriter, req *http.Request) { | |
ctx := req.Context() | |
identityGetResp, _, err := client.PlaidApi.IdentityGet(ctx).IdentityGetRequest( | |
*plaid.NewIdentityGetRequest(accessToken), | |
).Execute() | |
if err != nil { | |
renderError(err) | |
return | |
} | |
var data map[string]interface{} | |
data["identity"] = identityGetResp.GetAccounts() | |
res.WriteHeader(http.StatusOK) | |
if err := json.NewEncoder(res).Encode(data); err != nil { | |
renderError(err) | |
} | |
} | |
} | |
func Transactions() func(w http.ResponseWriter, r *http.Request) { | |
return func(res http.ResponseWriter, req *http.Request) { | |
ctx := req.Context() | |
// pull transactions for the past 30 days | |
endDate := time.Now().Local().Format("2006-01-02") | |
startDate := time.Now().Local().Add(-30 * 24 * time.Hour).Format("2006-01-02") | |
transactionsResp, _, err := client.PlaidApi.TransactionsGet(ctx).TransactionsGetRequest( | |
*plaid.NewTransactionsGetRequest( | |
accessToken, | |
startDate, | |
endDate, | |
), | |
).Execute() | |
if err != nil { | |
renderError(err) | |
return | |
} | |
var data map[string]interface{} | |
data["accounts"] = transactionsResp.GetAccounts() | |
data["transactions"] = transactionsResp.GetTransactions() | |
res.WriteHeader(http.StatusOK) | |
if err := json.NewEncoder(res).Encode(data); err != nil { | |
renderError(err) | |
} | |
} | |
} | |
// This functionality is only relevant for the UK Payment Initiation product. | |
// Retrieve Payment for a specified Payment ID | |
func Payment() func(w http.ResponseWriter, r *http.Request) { | |
return func(res http.ResponseWriter, req *http.Request) { | |
ctx := req.Context() | |
paymentGetResp, _, err := client.PlaidApi.PaymentInitiationPaymentGet(ctx).PaymentInitiationPaymentGetRequest( | |
*plaid.NewPaymentInitiationPaymentGetRequest(paymentID), | |
).Execute() | |
if err != nil { | |
renderError(err) | |
return | |
} | |
var data map[string]interface{} | |
data["payment"] = paymentGetResp | |
res.WriteHeader(http.StatusOK) | |
if err := json.NewEncoder(res).Encode(data); err != nil { | |
renderError(err) | |
} | |
} | |
} | |
func InvestmentTransactions() func(w http.ResponseWriter, r *http.Request) { | |
return func(res http.ResponseWriter, req *http.Request) { | |
ctx := req.Context() | |
endDate := time.Now().Local().Format("2006-01-02") | |
startDate := time.Now().Local().Add(-30 * 24 * time.Hour).Format("2006-01-02") | |
request := plaid.NewInvestmentsTransactionsGetRequest(accessToken, startDate, endDate) | |
invTxResp, _, err := client.PlaidApi.InvestmentsTransactionsGet(ctx).InvestmentsTransactionsGetRequest(*request).Execute() | |
if err != nil { | |
renderError(err) | |
return | |
} | |
var data map[string]interface{} | |
data["investment_transactions"] = invTxResp | |
res.WriteHeader(http.StatusOK) | |
if err := json.NewEncoder(res).Encode(data); err != nil { | |
renderError(err) | |
} | |
} | |
} | |
func Holdings() func(w http.ResponseWriter, r *http.Request) { | |
return func(res http.ResponseWriter, req *http.Request) { | |
ctx := req.Context() | |
holdingsGetResp, _, err := client.PlaidApi.InvestmentsHoldingsGet(ctx).InvestmentsHoldingsGetRequest( | |
*plaid.NewInvestmentsHoldingsGetRequest(accessToken), | |
).Execute() | |
if err != nil { | |
renderError(err) | |
return | |
} | |
var data map[string]interface{} | |
data["holdings"] = holdingsGetResp | |
res.WriteHeader(http.StatusOK) | |
if err := json.NewEncoder(res).Encode(data); err != nil { | |
renderError(err) | |
} | |
} | |
} | |
/* | |
func info(context *gin.Context) { | |
context.JSON(http.StatusOK, map[string]interface{}{ | |
"item_id": itemID, | |
"access_token": accessToken, | |
"products": strings.Split(PLAID_PRODUCTS, ","), | |
}) | |
} | |
*/ | |
func CreatePublicToken() func(w http.ResponseWriter, r *http.Request) { | |
return func(res http.ResponseWriter, req *http.Request) { | |
ctx := req.Context() | |
// Create a one-time use public_token for the Item. | |
// This public_token can be used to initialize Link in update mode for a user | |
publicTokenCreateResp, _, err := client.PlaidApi.ItemCreatePublicToken(ctx).ItemPublicTokenCreateRequest( | |
*plaid.NewItemPublicTokenCreateRequest(accessToken), | |
).Execute() | |
if err != nil { | |
renderError(err) | |
return | |
} | |
var data map[string]interface{} | |
data["public_token"] = publicTokenCreateResp.GetPublicToken() | |
res.WriteHeader(http.StatusOK) | |
if err := json.NewEncoder(res).Encode(data); err != nil { | |
renderError(err) | |
} | |
} | |
} | |
func CreateLinkToken() func(w http.ResponseWriter, r *http.Request) { | |
return func(res http.ResponseWriter, req *http.Request) { | |
ctx := req.Context() | |
linkToken, err := linkTokenCreate(ctx, nil) | |
if err != nil { | |
renderError(err) | |
return | |
} | |
var data map[string]interface{} | |
data["link_token"] = linkToken | |
res.WriteHeader(http.StatusOK) | |
if err := json.NewEncoder(res).Encode(data); err != nil { | |
renderError(err) | |
} | |
} | |
} | |
func convertCountryCodes(countryCodeStrs []string) []plaid.CountryCode { | |
countryCodes := []plaid.CountryCode{} | |
for _, countryCodeStr := range countryCodeStrs { | |
countryCodes = append(countryCodes, plaid.CountryCode(countryCodeStr)) | |
} | |
return countryCodes | |
} | |
func convertProducts(productStrs []string) []plaid.Products { | |
products := []plaid.Products{} | |
for _, productStr := range productStrs { | |
products = append(products, plaid.Products(productStr)) | |
} | |
return products | |
} | |
// linkTokenCreate creates a link token using the specified parameters | |
func linkTokenCreate(ctx context.Context, | |
paymentInitiation *plaid.LinkTokenCreateRequestPaymentInitiation, | |
) (string, error) { | |
countryCodes := convertCountryCodes(strings.Split(PLAID_COUNTRY_CODES, ",")) | |
products := convertProducts(strings.Split(PLAID_PRODUCTS, ",")) | |
redirectURI := PLAID_REDIRECT_URI | |
user := plaid.LinkTokenCreateRequestUser{ | |
ClientUserId: time.Now().String(), | |
} | |
request := plaid.NewLinkTokenCreateRequest( | |
"Plaid Quickstart", | |
"en", | |
countryCodes, | |
user, | |
) | |
request.SetProducts(products) | |
if redirectURI != "" { | |
request.SetRedirectUri(redirectURI) | |
} | |
if paymentInitiation != nil { | |
request.SetPaymentInitiation(*paymentInitiation) | |
} | |
linkTokenCreateResp, _, err := client.PlaidApi.LinkTokenCreate(ctx).LinkTokenCreateRequest(*request).Execute() | |
if err != nil { | |
return "", err | |
} | |
return linkTokenCreateResp.GetLinkToken(), nil | |
} | |
func Assets() func(w http.ResponseWriter, r *http.Request) { | |
return func(res http.ResponseWriter, req *http.Request) { | |
ctx := req.Context() | |
// create the asset report | |
assetReportCreateResp, _, err := client.PlaidApi.AssetReportCreate(ctx).AssetReportCreateRequest( | |
*plaid.NewAssetReportCreateRequest([]string{accessToken}, 10), | |
).Execute() | |
if err != nil { | |
renderError(err) | |
return | |
} | |
assetReportToken := assetReportCreateResp.GetAssetReportToken() | |
// get the asset report | |
assetReportGetResp, err := pollForAssetReport(ctx, client, assetReportToken) | |
if err != nil { | |
renderError(err) | |
return | |
} | |
// get it as a pdf | |
pdfRequest := plaid.NewAssetReportPDFGetRequest(assetReportToken) | |
pdfFile, _, err := client.PlaidApi.AssetReportPdfGet(ctx).AssetReportPDFGetRequest(*pdfRequest).Execute() | |
if err != nil { | |
renderError(err) | |
return | |
} | |
reader := bufio.NewReader(pdfFile) | |
content, err := ioutil.ReadAll(reader) | |
if err != nil { | |
renderError(err) | |
return | |
} | |
// convert pdf to base64 | |
encodedPdf := base64.StdEncoding.EncodeToString(content) | |
var data map[string]interface{} | |
data["pdf"] = encodedPdf | |
data["json"] = assetReportGetResp.GetReport() | |
res.WriteHeader(http.StatusOK) | |
if err := json.NewEncoder(res).Encode(data); err != nil { | |
renderError(err) | |
} | |
} | |
} | |
func pollForAssetReport(ctx context.Context, client *plaid.APIClient, assetReportToken string) (*plaid.AssetReportGetResponse, error) { | |
numRetries := 20 | |
request := plaid.NewAssetReportGetRequest(assetReportToken) | |
for i := 0; i < numRetries; i++ { | |
response, _, err := client.PlaidApi.AssetReportGet(ctx).AssetReportGetRequest(*request).Execute() | |
if err != nil { | |
plaidErr, err := plaid.ToPlaidError(err) | |
if plaidErr.ErrorCode == "PRODUCT_NOT_READY" { | |
time.Sleep(1 * time.Second) | |
continue | |
} else { | |
return nil, err | |
} | |
} else { | |
return &response, nil | |
} | |
} | |
return nil, errors.New("Timed out when polling for an asset report.") | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment