Go paypal REST API request - paypal

So Im currently using Go and Im trying to create a payment for Paypal I been trying this code
payer := &Payer{"paypal"}
amount := &Amount{"EUR", "12"}
trans := &Transactions{amount, "A super test"}
uris := &Redirect_urls{"http://localhost", "http://localhost"}
p := &Payment{"sale", payer, trans, uris}
response, err := json.Marshal(p)
if err != nil {
log.Println("Error at PaypalPayment - buy controller")
log.Fatal(err)
}
log.Println(string(response))
client := &http.Client{}
buf := bytes.NewBuffer(response)
req, err := http.NewRequest("POST", "https://api.sandbox.paypal.com/v1/payments/payment", buf)
if err != nil {
log.Println("Error at PaypalPayment - buy controller - 2")
log.Fatal(err)
}
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Authorization", "Bearer " + token.Access_token)
resp, err := client.Do(req)
if err != nil {
log.Println("Error at PaypalPayment - buy controller - 3")
log.Fatal(err)
}
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Println("Error at PaypalPayment - buy controller - 4")
log.Fatal(err)
}
log.Println(string(body))
I already got the access token, the problem is Im getting this error on the response body (last line)
MALFORMED_REQUEST
The request Im using is this (as of the println)
{
"Intent":"sale",
"Payer":{
"Payment_method":"paypal"
},
"Transactions":{
"Amount":{
"Currency":"EUR",
"Total":"12"
},
"Description":"Super test"
},
"Redirect_urls":{
"Return_url":"http://localhost",
"Cancel_url":"http://localhost"
}
}
At my eyes seems a good request... no idea what im missing

As pointed out by #jcbwlkr you're casing doesn't match what is in the docs. If you don't have json tags on your types you'll have to add them. You have to keep the property names uppercase in Go because it's what marks the fields as being exported. If you're not familiar with this do a search for 'unexported vs exported fields golang'
For example your Payment structs definition needs to look like this;
type Payment struct {
Amount *Amount `json:"amount"`
Transactions *Transactions `json:"transactions"`
RdUrls *Redirect_urls `json:"redirect_urls"`
}
Also, just fyi you can use nest those declarations where you declare the payment so you don't have to assign to local instances of Amount, Transactions and Redirect_urls in order to do the declaration.
It's just like;
p := &Payment{"sale", payer, &Transactions{amount, "A super test"}, uris}

The problem was transactions needed to be an array. how blind I am
Transactions []*Transactions `json:"transactions"`

Related

Go Echo: POST Method gives Error "Method not allowed"

Building an app with echo and basically created some routes.
The GET ones are working fine, but the post one is give me the error:
Do not really understand where the error lies here.
{...."method":"GET","uri":"/addPerson", message=Method Not Allowed","...."bytes_in":0,"bytes_out":33}
main.go snippet
func initEchoServer() {
e := echo.New()
e.Use(middleware.Logger())
e.Use(middleware.Recover())
// get all persons
e.GET("/persons", Info)
// get specific id
e.GET("/persons/:id", getPerson)
e.POST("/addPerson", addPerson)
e.Logger.Fatal(e.Start(viper.GetString("port")))
}
func addPerson(c echo.Context) error {
ctx := context.Background()
db, err := sql.Open("postgres", "host=postgres port=5432 user=postgres dbname=postgres password=postgres sslmode=disable")
if err != nil {
log.Fatal(err)
}
queries := postgres.New(db)
insertedPerson, err := queries.CreatePersons(ctx, postgres.CreatePersonsParams{
Firstname: "Mike",
Lastname: "Jordan",
})
if err != nil {
log.Errorf("Failed to insert a person %v", err)
return err
}
fmt.Println(insertedPerson)
return c.JSONPretty(http.StatusOK, insertedPerson, " ")
}
queries.sql.go snippet
type CreatePersonsParams struct {
Firstname string
Lastname string
}
func (q *Queries) CreatePersons(ctx context.Context, arg CreatePersonsParams) (Person, error) {
row := q.db.QueryRowContext(ctx, createPersons, arg.Firstname, arg.Lastname)
var i Person
err := row.Scan(&i.ID, &i.Firstname, &i.Lastname)
return i, err
}
you're use post method in routers
e.POST("/addPerson", addPerson)
You can use postman to hit API using POST method, don't use browser
If you register routes with POST in echo, it will only register POST method on that path. But it seems that you GET that path.
You can use e.GET().

ObjectIdFromHex invalid byte error on identical strings

I'm trying to implement a FindOne method in my Golang REST API. The trouble comes where i have to search by ID. I have to convert the ID into something readable by the database, so i use primitive.ObjectIDFromHex(id)
The problem is that this method throws an error :
2021/06/19 06:56:15 encoding/hex: invalid byte: U+000A
ONLY when i call it with the id that comes from my URL GET params.
I did two versions : one with hard-coded ID, and one with GET ID. See code below.
func Admin(id string) (bson.M, error) {
coll, err := db.ConnectToCollection("admin")
if err != nil {
log.Fatal(err)
}
var admin bson.M
HardCoded := "60cb275c074ab46a1aeda45e"
fmt.Println(HardCoded) // Just to be sure : the two strings seem identical
fmt.Println(id)
objetId, err := primitive.ObjectIDFromHex(id) // throws encoding error
// objetId, err := primitive.ObjectIDFromHex(HardCoded) // Doesnt throw encoding err
if err != nil {
log.Fatal(err)
}
var ctx = context.TODO()
if err := coll.FindOne(ctx, bson.M{"_id": objetId}).Decode(&admin); err != nil {
log.Fatal(err)
}
return admin, nil
}
Of course, you'll want to know where the param id comes from.
Here you go :
func GetAdmin(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
params := mux.Vars(r)
admin, err := Admin(params["id"]) // Calling the Admin function above
if err != nil {
fmt.Println(err)
http.Error(w, err.Error(), http.StatusUnauthorized)
} else {
JSON, err := json.Marshal(admin)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
w.Write(JSON)
}
}
Trim the line feed from the end of id:
id = strings.TrimSpace(id)
Use the %q format verb when debugging issues like this. The line feed is clearly visible in this output:
fmt.Printf("%q\n", HardCoded) // prints "60cb275c074ab46a1aeda45e"
fmt.Printf("%q\n", id) // prints "60cb275c074ab46a1aeda45e\n"

POST data faild using http.NewRequest

I am trying to pass data from one golang service to another using http.NewRequest(). To do it I used following code:
httpClient := http.Client{}
userserviceUrl := "http://user:7071/checkemail"
form := url.Values{}
form.Set("uuid", uuid)
form.Set("email", email)
b := bytes.NewBufferString(form.Encode())
req, err := http.NewRequest("POST", userserviceUrl, b)
if err != nil {
log.Println(err)
}
opentracing.GlobalTracer().Inject(
validateEmailSpan.Context(),
opentracing.HTTPHeaders,
opentracing.HTTPHeadersCarrier(req.Header))
resp, err := httpClient.Do(req)
//_, err = http.PostForm("http://user:7071/checkemail", url.Values{"uuid": {uuid}, "email": {email}})
if err != nil {
log.Println("Couldnt verify email address user service sends an error : ", err)
}
defer resp.Body.Close()
I got this from Golang: http.NewRequest POST
When I try to dump received data from user service:
req.ParseForm()
log.Println("Form values : ", req.Form)
I get an empty map[]
Here I just try to inject tracing span to my request, previously I have used http.PostForm() to pass data, it worked perfectly. But I have no idea to pass tracing to it.
From the docs for ParseForm:
[...] when the Content-Type is not application/x-www-form-urlencoded, the request Body is not read, and r.PostForm is initialized to a non-nil, empty value.
PostForm sets the Content-Type automatically, but now you have to do it yourself:
req, err := http.NewRequest("POST", userserviceUrl, strings.NewReader(form.Encode()))
// TODO: handle error
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")

How to send an e-mail from Go

I'm trying to send an e-mail in Golang and I have a lot of problems with it. I'm new in Go so maybe this is very simply but I cannot find the answer on the doc.
This is what I want to do:
1. get an e-mail from the STDIN
2. parse the e-mail (getting from, to, subject, attachments and so on)
3. send this e-mail (put it again to the queue in local postfix)
I did 1 and 2 but I have a problem with 3th one.
This is what I have now:
package main
import (
"fmt"
"github.com/jhillyerd/go.enmime"
//"github.com/sendgrid/sendgrid-go"
"net/smtp"
"github.com/jordan-wright/email"
"os"
"net/mail"
"io/ioutil"
"bytes"
)
func main() {
mail_stdin, err := ioutil.ReadAll(os.Stdin)
if err != nil {
return
}
// Convert type to io.Reader
buf := bytes.NewBuffer(mail_stdin)
msg, err := mail.ReadMessage(buf)
if err != nil {
return
}
mime, err := enmime.ParseMIMEBody(msg)
if err != nil {
return
}
# saving attachments
for _, value := range mime.Attachments {
fmt.Println(value.FileName())
err := ioutil.WriteFile(value.FileName(), value.Content(), 0664)
if err != nil {
//panic(err)
return
}
fmt.Printf("From: %v\n", msg.Header.Get("From"))
fmt.Printf("Subject: %v\n", mime.GetHeader("Subject"))
fmt.Printf("Text Body: %v chars\n", len(mime.Text))
fmt.Printf("HTML Body: %v chars\n", len(mime.Html))
fmt.Printf("Inlines: %v\n", len(mime.Inlines))
fmt.Printf("Attachments: %v\n", len(mime.Attachments))
fmt.Println(mime.Attachments)
fmt.Println(mime.OtherParts)
fmt.Printf("Attachments: %v\n", mime.Attachments)
}
I already did few tests using: net/smtp, sendgrid-go and jordan-wright/email.
All I want to do is to send an e-mail (without changing anything) from the server to the queue again. Most of those modules needs to have Auth, but I just want to simply send is using sendmail, in the same way as I can do this from the bash:
# echo "test" | mail {address}
Using net/smtp you can do this fairly easily... Assuming you have an smtp server running that you can connect to without authentication. I would guess for what you're trying to accomplish it's actually a lot easier to do through something simple like your gmail ( https://www.digitalocean.com/community/tutorials/how-to-use-google-s-smtp-server )
Anyway, here's a couple code samples to cover either case;
c, err := smtp.Dial("mail.example.com:25")
if err != nil {
log.Fatal(err)
}
defer c.Close()
// Set the sender and recipient.
c.Mail("sender#example.org")
c.Rcpt("recipient#example.net")
// Send the email body.
wc, err := c.Data()
if err != nil {
log.Fatal(err)
}
defer wc.Close()
buf := bytes.NewBufferString("This is the email body.")
if _, err = buf.WriteTo(wc); err != nil {
log.Fatal(err)
}
Alternatively here's a go playground example that uses simple auth; http://play.golang.org/p/ATDCgJGKZ3 unless you've already got an smtp server running on your dev box following something like that will probably be a lot easier.

How to read from request then use that result to do POST request then process its results

I'm trying to read from request then use that result to do POST request to another endpoint then process its results then return its results in JSON.
I have below code so far:
// POST
func (u *UserResource) authenticate(request *restful.Request, response *restful.Response) {
Api := Api{url: "http://api.com/api"}
usr := new(User)
err := request.ReadEntity(&usr)
if err != nil {
response.WriteErrorString(http.StatusInternalServerError, err.Error())
return
}
api_resp, err := http.Post(Api.url, "text/plain", bytes.NewBuffer(usr))
if err != nil {
response.WriteErrorString(http.StatusInternalServerError, err.Error())
return
}
defer api_resp.Body.Close()
body, err := ioutil.ReadAll(api_resp.Body)
response.WriteHeader(http.StatusCreated)
err = xml.Unmarshal(body, usr)
if err != nil {
fmt.Printf("error: %v", err)
return
}
// result, err := json.Marshal(usr)
// response.Write(result)
response.WriteEntity(&usr)
fmt.Printf("Name: %q\n", usr.UserName)
}
I'm using Go Restful package for Writes and Reads.
I'm getting this error when I compile the file:
src\login.go:59: cannot use usr (type *User) as type []byte in argument to bytes.NewBuffer
What would be the best way to solve this issue so I can do a POST with payload correctly?
You need to marshal your data structure to slice of bytes. Something like this:
usrXmlBytes, err := xml.Marshal(usr)
if err != nil {
response.WriteErrorString(http.StatusInternalServerError, err.Error())
return
}
api_resp, err := http.Post(Api.url, "text/plain", bytes.NewReader(usrXmlBytes))
http.Post takes an io.Reader as the third argument. You could implement io.Reader on your User type or more simply serialize your data and use the bytes pkg to to implement io.Reader
b, err := json.Marshal(usr)
if err != nil {
response.WriteErrorString(http.StatusInternalServerError, err.Error())
return
}
api_resp, err := http.Post(Api.url, "text/plain", bytes.NewReader(b))