diff --git a/controllers/payment.go b/controllers/payment.go index 0353fd51..c923e119 100644 --- a/controllers/payment.go +++ b/controllers/payment.go @@ -158,3 +158,17 @@ func (c *ApiController) NotifyPayment() { panic(fmt.Errorf("NotifyPayment() failed: %v", ok)) } } + +// @Title InvoicePayment +// @Tag Payment API +// @Description invoice payment +// @Param id query string true "The id of the payment" +// @Success 200 {object} controllers.Response The Response object +// @router /invoice-payment [post] +func (c *ApiController) InvoicePayment() { + id := c.Input().Get("id") + + payment := object.GetPayment(id) + c.Data["json"] = wrapActionResponse(object.InvoicePayment(payment)) + c.ServeJSON() +} diff --git a/object/payment.go b/object/payment.go index 31e7d02c..5c9ef5cf 100644 --- a/object/payment.go +++ b/object/payment.go @@ -53,6 +53,7 @@ type Payment struct { InvoiceTitle string `xorm:"varchar(100)" json:"invoiceTitle"` InvoiceTaxId string `xorm:"varchar(100)" json:"invoiceTaxId"` InvoiceRemark string `xorm:"varchar(100)" json:"invoiceRemark"` + InvoiceUrl string `xorm:"varchar(100)" json:"invoiceUrl"` } func GetPaymentCount(owner, field, value string) int { @@ -206,6 +207,46 @@ func NotifyPayment(request *http.Request, body []byte, owner string, providerNam return ok } +func invoicePayment(payment *Payment) (string, error) { + provider := getProvider(payment.Owner, payment.Provider) + if provider == nil { + return "", fmt.Errorf("the payment provider: %s does not exist", payment.Provider) + } + + pProvider, _, err := provider.getPaymentProvider() + if err != nil { + return "", err + } + + invoiceUrl, err := pProvider.GetInvoice(payment.Name, payment.PersonName, payment.PersonIdCard, payment.PersonEmail, payment.PersonPhone, payment.InvoiceType, payment.InvoiceTitle, payment.InvoiceTaxId) + if err != nil { + return "", err + } + + return invoiceUrl, nil +} + +func InvoicePayment(payment *Payment) bool { + if payment.State != "Paid" { + return false + } + + invoiceUrl, err := invoicePayment(payment) + + if err != nil { + payment.State = "Error" + payment.Message = err.Error() + } else { + payment.State = "Invoiced" + payment.InvoiceUrl = invoiceUrl + } + + UpdatePayment(payment.GetId(), payment) + + ok := err == nil + return ok +} + func (payment *Payment) GetId() string { return fmt.Sprintf("%s/%s", payment.Owner, payment.Name) } diff --git a/pp/alipay.go b/pp/alipay.go index 88bfe00e..aaabc1b2 100644 --- a/pp/alipay.go +++ b/pp/alipay.go @@ -90,3 +90,7 @@ func (pp *AlipayPaymentProvider) Notify(request *http.Request, body []byte, auth return productDisplayName, paymentName, price, productName, providerName, nil } + +func (pp *AlipayPaymentProvider) GetInvoice(paymentName string, personName string, personIdCard string, personEmail string, personPhone string, invoiceType string, invoiceTitle string, invoiceTaxId string) (string, error) { + return "", nil +} diff --git a/pp/gc.go b/pp/gc.go index 07008626..abf2fcd7 100644 --- a/pp/gc.go +++ b/pp/gc.go @@ -87,6 +87,27 @@ type GcResponseBody struct { Sign string `json:"sign"` } +type GcInvoiceReqInfo struct { + BusNo string `json:"busno"` + PayerName string `json:"payername"` + IdNum string `json:"idnum"` + PayerType string `json:"payertype"` + InvoiceTitle string `json:"invoicetitle"` + Tin string `json:"tin"` + Phone string `json:"phone"` + Email string `json:"email"` +} + +type GcInvoiceRespInfo struct { + BusNo string `json:"busno"` + State string `json:"state"` + EbillCode string `json:"ebillcode"` + EbillNo string `json:"ebillno"` + CheckCode string `json:"checkcode"` + Url string `json:"url"` + Content string `json:"content"` +} + func NewGcPaymentProvider(clientId string, clientSecret string, host string) *GcPaymentProvider { pp := &GcPaymentProvider{} @@ -230,3 +251,70 @@ func (pp *GcPaymentProvider) Notify(request *http.Request, body []byte, authorit return productDisplayName, paymentName, price, productName, providerName, nil } + +func (pp *GcPaymentProvider) GetInvoice(paymentName string, personName string, personIdCard string, personEmail string, personPhone string, invoiceType string, invoiceTitle string, invoiceTaxId string) (string, error) { + payerType := "0" + if invoiceType == "Organization" { + payerType = "1" + } + + invoiceReqInfo := GcInvoiceReqInfo{ + BusNo: paymentName, + PayerName: personName, + IdNum: personIdCard, + PayerType: payerType, + InvoiceTitle: invoiceTitle, + Tin: invoiceTaxId, + Phone: personPhone, + Email: personEmail, + } + + b, err := json.Marshal(invoiceReqInfo) + if err != nil { + return "", err + } + + body := GcRequestBody{ + Op: "InvoiceEBillByOrder", + Xmpch: pp.Xmpch, + Version: "1.4", + Data: base64.StdEncoding.EncodeToString(b), + RequestTime: util.GenerateSimpleTimeId(), + } + + params := fmt.Sprintf("data=%s&op=%s&requesttime=%s&version=%s&xmpch=%s%s", body.Data, body.Op, body.RequestTime, body.Version, body.Xmpch, pp.SecretKey) + body.Sign = strings.ToUpper(util.GetMd5Hash(params)) + + bodyBytes, err := json.Marshal(body) + if err != nil { + return "", err + } + + respBytes, err := pp.doPost(bodyBytes) + if err != nil { + return "", err + } + + var respBody GcResponseBody + err = json.Unmarshal(respBytes, &respBody) + if err != nil { + return "", err + } + + if respBody.ReturnCode != "SUCCESS" { + return "", fmt.Errorf("%s: %s", respBody.ReturnCode, respBody.ReturnMsg) + } + + invoiceRespInfoBytes, err := base64.StdEncoding.DecodeString(respBody.Data) + if err != nil { + return "", err + } + + var invoiceRespInfo GcInvoiceRespInfo + err = json.Unmarshal(invoiceRespInfoBytes, &invoiceRespInfo) + if err != nil { + return "", err + } + + return invoiceRespInfo.Url, nil +} diff --git a/pp/provider.go b/pp/provider.go index 804821c4..fbd92684 100644 --- a/pp/provider.go +++ b/pp/provider.go @@ -19,6 +19,7 @@ import "net/http" type PaymentProvider interface { Pay(providerName string, productName string, paymentName string, productDisplayName string, price float64, returnUrl string, notifyUrl string) (string, error) Notify(request *http.Request, body []byte, authorityPublicKey string) (string, string, float64, string, string, error) + GetInvoice(paymentName string, personName string, personIdCard string, personEmail string, personPhone string, invoiceType string, invoiceTitle string, invoiceTaxId string) (string, error) } func GetPaymentProvider(typ string, appId string, clientSecret string, host string, appPublicKey string, appPrivateKey string, authorityPublicKey string, authorityRootPublicKey string) PaymentProvider { diff --git a/routers/router.go b/routers/router.go index c9ae37f9..5b49fdbb 100644 --- a/routers/router.go +++ b/routers/router.go @@ -167,6 +167,7 @@ func initAPI() { beego.Router("/api/add-payment", &controllers.ApiController{}, "POST:AddPayment") beego.Router("/api/delete-payment", &controllers.ApiController{}, "POST:DeletePayment") beego.Router("/api/notify-payment/?:owner/?:provider/?:product/?:payment", &controllers.ApiController{}, "POST:NotifyPayment") + beego.Router("/api/invoice-payment", &controllers.ApiController{}, "POST:InvoicePayment") beego.Router("/api/send-email", &controllers.ApiController{}, "POST:SendEmail") beego.Router("/api/send-sms", &controllers.ApiController{}, "POST:SendSms") diff --git a/web/src/PaymentEditPage.js b/web/src/PaymentEditPage.js index 1813e812..e1e339ca 100644 --- a/web/src/PaymentEditPage.js +++ b/web/src/PaymentEditPage.js @@ -62,6 +62,20 @@ class PaymentEditPage extends React.Component { }); } + issueInvoice() { + const errorText = this.checkError(); + if (errorText !== "") { + Setting.showMessage("error", errorText); + return; + } + + alert("111") + } + + downloadInvoice() { + Setting.openLinkSafe(this.state.payment.invoiceUrl); + } + renderPayment() { return ( - { + { this.updatePaymentField('personName', e.target.value); if (this.state.payment.invoiceType === "Individual") { this.updatePaymentField('invoiceTitle', e.target.value); @@ -191,7 +205,7 @@ class PaymentEditPage extends React.Component { {Setting.getLabel(i18next.t("payment:Person ID card"), i18next.t("payment:Person ID card - Tooltip"))} : - { + { this.updatePaymentField('personIdCard', e.target.value); }} /> @@ -201,7 +215,7 @@ class PaymentEditPage extends React.Component { {Setting.getLabel(i18next.t("payment:Person Email"), i18next.t("payment:Person Email - Tooltip"))} : - { + { this.updatePaymentField('personEmail', e.target.value); }} /> @@ -211,7 +225,7 @@ class PaymentEditPage extends React.Component { {Setting.getLabel(i18next.t("payment:Person phone"), i18next.t("payment:Person phone - Tooltip"))} : - { + { this.updatePaymentField('personPhone', e.target.value); }} /> @@ -221,7 +235,7 @@ class PaymentEditPage extends React.Component { {Setting.getLabel(i18next.t("payment:Invoice type"), i18next.t("payment:Invoice type - Tooltip"))} : - { this.updatePaymentField('invoiceType', value); if (value === "Individual") { this.updatePaymentField('invoiceTitle', this.state.payment.personName); @@ -242,7 +256,7 @@ class PaymentEditPage extends React.Component { {Setting.getLabel(i18next.t("payment:Invoice title"), i18next.t("payment:Invoice title - Tooltip"))} : - { + { this.updatePaymentField('invoiceTitle', e.target.value); }} /> @@ -252,7 +266,7 @@ class PaymentEditPage extends React.Component { {Setting.getLabel(i18next.t("payment:Invoice Tax ID"), i18next.t("payment:Invoice Tax ID - Tooltip"))} : - { + { this.updatePaymentField('invoiceTaxId', e.target.value); }} /> @@ -262,11 +276,35 @@ class PaymentEditPage extends React.Component { {Setting.getLabel(i18next.t("payment:Invoice remark"), i18next.t("payment:Invoice remark - Tooltip"))} : - { + { this.updatePaymentField('invoiceRemark', e.target.value); }} /> + + + {Setting.getLabel(i18next.t("payment:Invoice URL"), i18next.t("payment:Invoice URL - Tooltip"))} : + + + { + this.updatePaymentField('invoiceUrl', e.target.value); + }} /> + + + + + {Setting.getLabel(i18next.t("payment:Invoice actions"), i18next.t("payment:Invoice actions - Tooltip"))} : + + + { + this.state.payment.invoiceUrl === "" ? ( + + ) : ( + + ) + } + + ) } @@ -293,7 +331,7 @@ class PaymentEditPage extends React.Component { } if (this.state.payment.invoiceType === "Individual") { - if (this.state.payment.invoiceTitle !== "") { + if (this.state.payment.invoiceTitle !== this.state.payment.personName) { return i18next.t("signup:The input is not invoice title!"); } @@ -301,7 +339,7 @@ class PaymentEditPage extends React.Component { return i18next.t("signup:The input is not invoice Tax ID!"); } } else { - if (!Setting.isValidInvoiceTitle(this.state.payment.invoiceTitle)) { + if (this.state.payment.invoiceTitle === "" || !Setting.isValidInvoiceTitle(this.state.payment.invoiceTitle)) { return i18next.t("signup:The input is not invoice title!"); } diff --git a/web/src/Setting.js b/web/src/Setting.js index 72ba9ea9..1b3c7735 100644 --- a/web/src/Setting.js +++ b/web/src/Setting.js @@ -300,6 +300,15 @@ export function openLink(link) { w.location.href = link; } +export function openLinkSafe(link) { + // Javascript window.open issue in safari + // https://stackoverflow.com/questions/45569893/javascript-window-open-issue-in-safari + let a = document.createElement('a'); + a.href = link; + a.setAttribute('target', '_blank'); + a.click(); +} + export function goToLink(link) { window.location.href = link; } diff --git a/web/src/locales/de/data.json b/web/src/locales/de/data.json index 1833a8e2..1c7d5895 100644 --- a/web/src/locales/de/data.json +++ b/web/src/locales/de/data.json @@ -260,16 +260,22 @@ "payment": { "Currency": "Currency", "Currency - Tooltip": "Currency - Tooltip", + "Download Invoice": "Download Invoice", "Edit Payment": "Edit Payment", "Individual": "Individual", "Invoice Tax ID": "Invoice Tax ID", "Invoice Tax ID - Tooltip": "Invoice Tax ID - Tooltip", + "Invoice URL": "Invoice URL", + "Invoice URL - Tooltip": "Invoice URL - Tooltip", + "Invoice actions": "Invoice actions", + "Invoice actions - Tooltip": "Invoice actions - Tooltip", "Invoice remark": "Invoice remark", "Invoice remark - Tooltip": "Invoice remark - Tooltip", "Invoice title": "Invoice title", "Invoice title - Tooltip": "Invoice title - Tooltip", "Invoice type": "Invoice type", "Invoice type - Tooltip": "Invoice type - Tooltip", + "Issue Invoice": "Issue Invoice", "Message": "Message", "Message - Tooltip": "Message - Tooltip", "New Payment": "New Payment", diff --git a/web/src/locales/en/data.json b/web/src/locales/en/data.json index cce9a2c9..3b296bd5 100644 --- a/web/src/locales/en/data.json +++ b/web/src/locales/en/data.json @@ -260,16 +260,22 @@ "payment": { "Currency": "Currency", "Currency - Tooltip": "Currency - Tooltip", + "Download Invoice": "Download Invoice", "Edit Payment": "Edit Payment", "Individual": "Individual", "Invoice Tax ID": "Invoice Tax ID", "Invoice Tax ID - Tooltip": "Invoice Tax ID - Tooltip", + "Invoice URL": "Invoice URL", + "Invoice URL - Tooltip": "Invoice URL - Tooltip", + "Invoice actions": "Invoice actions", + "Invoice actions - Tooltip": "Invoice actions - Tooltip", "Invoice remark": "Invoice remark", "Invoice remark - Tooltip": "Invoice remark - Tooltip", "Invoice title": "Invoice title", "Invoice title - Tooltip": "Invoice title - Tooltip", "Invoice type": "Invoice type", "Invoice type - Tooltip": "Invoice type - Tooltip", + "Issue Invoice": "Issue Invoice", "Message": "Message", "Message - Tooltip": "Message - Tooltip", "New Payment": "New Payment", diff --git a/web/src/locales/fr/data.json b/web/src/locales/fr/data.json index 49961334..2790f6ec 100644 --- a/web/src/locales/fr/data.json +++ b/web/src/locales/fr/data.json @@ -260,16 +260,22 @@ "payment": { "Currency": "Currency", "Currency - Tooltip": "Currency - Tooltip", + "Download Invoice": "Download Invoice", "Edit Payment": "Edit Payment", "Individual": "Individual", "Invoice Tax ID": "Invoice Tax ID", "Invoice Tax ID - Tooltip": "Invoice Tax ID - Tooltip", + "Invoice URL": "Invoice URL", + "Invoice URL - Tooltip": "Invoice URL - Tooltip", + "Invoice actions": "Invoice actions", + "Invoice actions - Tooltip": "Invoice actions - Tooltip", "Invoice remark": "Invoice remark", "Invoice remark - Tooltip": "Invoice remark - Tooltip", "Invoice title": "Invoice title", "Invoice title - Tooltip": "Invoice title - Tooltip", "Invoice type": "Invoice type", "Invoice type - Tooltip": "Invoice type - Tooltip", + "Issue Invoice": "Issue Invoice", "Message": "Message", "Message - Tooltip": "Message - Tooltip", "New Payment": "New Payment", diff --git a/web/src/locales/ja/data.json b/web/src/locales/ja/data.json index 16294912..07063aec 100644 --- a/web/src/locales/ja/data.json +++ b/web/src/locales/ja/data.json @@ -260,16 +260,22 @@ "payment": { "Currency": "Currency", "Currency - Tooltip": "Currency - Tooltip", + "Download Invoice": "Download Invoice", "Edit Payment": "Edit Payment", "Individual": "Individual", "Invoice Tax ID": "Invoice Tax ID", "Invoice Tax ID - Tooltip": "Invoice Tax ID - Tooltip", + "Invoice URL": "Invoice URL", + "Invoice URL - Tooltip": "Invoice URL - Tooltip", + "Invoice actions": "Invoice actions", + "Invoice actions - Tooltip": "Invoice actions - Tooltip", "Invoice remark": "Invoice remark", "Invoice remark - Tooltip": "Invoice remark - Tooltip", "Invoice title": "Invoice title", "Invoice title - Tooltip": "Invoice title - Tooltip", "Invoice type": "Invoice type", "Invoice type - Tooltip": "Invoice type - Tooltip", + "Issue Invoice": "Issue Invoice", "Message": "Message", "Message - Tooltip": "Message - Tooltip", "New Payment": "New Payment", diff --git a/web/src/locales/ko/data.json b/web/src/locales/ko/data.json index 13c7faf1..86ef673a 100644 --- a/web/src/locales/ko/data.json +++ b/web/src/locales/ko/data.json @@ -260,16 +260,22 @@ "payment": { "Currency": "Currency", "Currency - Tooltip": "Currency - Tooltip", + "Download Invoice": "Download Invoice", "Edit Payment": "Edit Payment", "Individual": "Individual", "Invoice Tax ID": "Invoice Tax ID", "Invoice Tax ID - Tooltip": "Invoice Tax ID - Tooltip", + "Invoice URL": "Invoice URL", + "Invoice URL - Tooltip": "Invoice URL - Tooltip", + "Invoice actions": "Invoice actions", + "Invoice actions - Tooltip": "Invoice actions - Tooltip", "Invoice remark": "Invoice remark", "Invoice remark - Tooltip": "Invoice remark - Tooltip", "Invoice title": "Invoice title", "Invoice title - Tooltip": "Invoice title - Tooltip", "Invoice type": "Invoice type", "Invoice type - Tooltip": "Invoice type - Tooltip", + "Issue Invoice": "Issue Invoice", "Message": "Message", "Message - Tooltip": "Message - Tooltip", "New Payment": "New Payment", diff --git a/web/src/locales/ru/data.json b/web/src/locales/ru/data.json index 7d24d957..539ddbf8 100644 --- a/web/src/locales/ru/data.json +++ b/web/src/locales/ru/data.json @@ -260,16 +260,22 @@ "payment": { "Currency": "Currency", "Currency - Tooltip": "Currency - Tooltip", + "Download Invoice": "Download Invoice", "Edit Payment": "Edit Payment", "Individual": "Individual", "Invoice Tax ID": "Invoice Tax ID", "Invoice Tax ID - Tooltip": "Invoice Tax ID - Tooltip", + "Invoice URL": "Invoice URL", + "Invoice URL - Tooltip": "Invoice URL - Tooltip", + "Invoice actions": "Invoice actions", + "Invoice actions - Tooltip": "Invoice actions - Tooltip", "Invoice remark": "Invoice remark", "Invoice remark - Tooltip": "Invoice remark - Tooltip", "Invoice title": "Invoice title", "Invoice title - Tooltip": "Invoice title - Tooltip", "Invoice type": "Invoice type", "Invoice type - Tooltip": "Invoice type - Tooltip", + "Issue Invoice": "Issue Invoice", "Message": "Message", "Message - Tooltip": "Message - Tooltip", "New Payment": "New Payment", diff --git a/web/src/locales/zh/data.json b/web/src/locales/zh/data.json index 7425af6f..fed6e7f6 100644 --- a/web/src/locales/zh/data.json +++ b/web/src/locales/zh/data.json @@ -260,16 +260,22 @@ "payment": { "Currency": "币种", "Currency - Tooltip": "如USD(美元),CNY(人民币)等", + "Download Invoice": "下载发票", "Edit Payment": "编辑付款", "Individual": "个人", "Invoice Tax ID": "纳税人识别号", "Invoice Tax ID - Tooltip": "开票类型为单位时,必须输入单位纳税人识别号;开票类型为个人时,不需要填写", + "Invoice URL": "发票URL", + "Invoice URL - Tooltip": "发票的下载地址URL", + "Invoice actions": "发票操作", + "Invoice actions - Tooltip": "操作包含开具发票和下载发票两种", "Invoice remark": "发票备注", "Invoice remark - Tooltip": "备注不超过50个字", "Invoice title": "发票抬头", "Invoice title - Tooltip": "开票类型为单位时,发票抬头可输入单位名称;开票类型为个人时,系统自动填写为缴费人姓名", "Invoice type": "开票类型", "Invoice type - Tooltip": "开票类型可以为个人或者单位", + "Issue Invoice": "开具发票", "Message": "消息", "Message - Tooltip": "付款处理结果消息", "New Payment": "添加付款",