Español · English
Librería de integración con la API de Transbank escrita en el lenguaje Go.
- Implementación
- Requisitos
- Instalación
- Primeros pasos
- Uso
- Manejo de errores
- Inyección de cliente HTTP
- Proyecto de ejemplo
| Servicio | Implementado | Documentación | Tests |
|---|---|---|---|
| Webpay Plus | ✅ | ✅ | ✅ |
| Webpay Plus Mall | ✅ | ✅ | ✅ |
| OneClick Mall | ✅ | ✅ | ✅ |
| Transacción Completa | ✅ | ✅ | ✅ |
| Transacción Completa Mall | ✅ | ✅ | ✅ |
| PatPass | ✅ | ✅ | ✅ |
- Go 1.24 o superior.
Asegúrese de que su proyecto use Go Modules (debe existir un archivo go.mod):
go mod initLuego obtenga el SDK:
go get github.com/ppastene/transbank-sdk-goDeclare sus credenciales y ambiente de Transbank en un transbank.Options{} y páselo como argumento al constructor del servicio que quiera utilizar.
import (
"github.com/ppastene/transbank-sdk-go"
"github.com/ppastene/transbank-sdk-go/webpayplus"
)
opts := transbank.Options{
CommerceCode: "597055555532", // su código de comercio
ApiKey: "579B532A7440BB0C9...", // su llave secreta
Environment: transbank.Integration, // o transbank.Production
}
tx, err := webpayplus.NewTransaction(opts)
if err != nil {
// manejar el error de construcción (validación de credenciales)
}Con eso ya puede usar los métodos del servicio indicado por la documentación de Transbank.
opts := transbank.Options{
CommerceCode: "597055555532",
ApiKey: "579B532A7440BB0C9079DED94D31EA1615BACEB56610332264630D42D0A36B1C",
Environment: transbank.Integration,
}
tx, err := webpayplus.NewTransaction(opts)
if err != nil {
// manejar el error
}
// Crear una transacción
create, err := tx.Create("orden-de-compra", "id-de-sesion", 10000.50, "https://www.mi-tienda.cl/retorno")
if err != nil {
// manejar el error
}
_ = create.Token // token de la transacción
_ = create.Url // URL del formulario de pago
// Confirmar una transacción
commit, err := tx.Commit("token")
if err != nil {
// manejar el error
}
// Consultar el estado de una transacción
status, err := tx.Status("token")
if err != nil {
// manejar el error
}
// Reembolsar una transacción
refund, err := tx.Refund("token", 10000.50)
if err != nil {
// manejar el error
}
// Capturar una transacción (solo en ambientes con captura diferida)
capture, err := tx.Capture("token", "orden-de-compra", "codigo-de-autorizacion", 10000.50)
if err != nil {
// manejar el error
}opts := transbank.Options{
CommerceCode: "597055555535", // código de comercio Mall
ApiKey: "579B532A7440BB0C9079DED94D31EA1615BACEB56610332264630D42D0A36B1C",
Environment: transbank.Integration,
}
mallTx, err := webpayplus.NewMallTransaction(opts)
if err != nil {
// manejar el error
}
details := []webpayplus.MallDetails{
{
Amount: 10000,
CommerceCode: "597055555536", // código de comercio de la tienda 1
BuyOrder: "orden-detalle-1234",
},
{
Amount: 10000,
CommerceCode: "597055555537", // código de comercio de la tienda 2
BuyOrder: "orden-detalle-4321",
},
}
create, err := mallTx.Create("orden-de-compra", "id-de-sesion", "https://www.mi-tienda.cl/retorno", details)
if err != nil {
// manejar el error
}
_ = create.Token
commit, err := mallTx.Commit("token")
if err != nil {
// manejar el error
}
status, err := mallTx.Status("token")
if err != nil {
// manejar el error
}
refund, err := mallTx.Refund("token", "orden-detalle-1234", "597055555536", 10000)
if err != nil {
// manejar el error
}
// Solo en ambientes con captura diferida
capture, err := mallTx.Capture("token", "597055555536", "orden-detalle-1234", "codigo-de-autorizacion", 10000)
if err != nil {
// manejar el error
}OneClick usa dos servicios: oneclick.NewMallInscription para inscribir/eliminar tarjetas y oneclick.NewMallTransaction para autorizar pagos, consultar estados, reembolsar y capturar.
import (
"github.com/ppastene/transbank-sdk-go"
"github.com/ppastene/transbank-sdk-go/oneclick"
)
opts := transbank.Options{
CommerceCode: "597055555541", // código de comercio OneClick
ApiKey: "579B532A7440BB0C9079DED94D31EA1615BACEB56610332264630D42D0A36B1C",
Environment: transbank.Integration,
}ins, err := oneclick.NewMallInscription(opts)
if err != nil {
// manejar el error
}
// Iniciar la inscripción de la tarjeta del cliente
start, err := ins.Start("nombre-de-usuario", "cliente@mi-tienda.cl", "https://www.mi-tienda.cl/retorno")
if err != nil {
// manejar el error
}
_ = start.Token // token de la inscripción
_ = start.UrlWebpay // URL del formulario de inscripción
// Confirmar la inscripción con el token recibido en el retorno (TBK_TOKEN)
finish, err := ins.Finish("token")
if err != nil {
// manejar el error
}
_ = finish.TbkUser // identificador de la tarjeta inscrita
// Eliminar una inscripción
err = ins.Delete("tbkUser", "nombre-de-usuario")
if err != nil {
// manejar el error
}tx, err := oneclick.NewMallTransaction(opts)
if err != nil {
// manejar el error
}
details := []oneclick.MallDetails{
{
Amount: 10000,
CommerceCode: "597055555542", // código de comercio de la tienda 1
BuyOrder: "orden-detalle-1234",
InstallmentsNumber: 3,
},
{
Amount: 50000,
CommerceCode: "597055555543", // código de comercio de la tienda 2
BuyOrder: "orden-detalle-4321",
InstallmentsNumber: 3,
},
}
// Autorizar un pago con la tarjeta inscrita
authorize, err := tx.Authorize("nombre-de-usuario", "tbkUser", "orden-de-compra", details)
if err != nil {
// manejar el error
}
_ = authorize.IsApproved() // true si todos los detalles fueron aprobados
// Consultar el estado de una transacción
status, err := tx.Status("orden-de-compra")
if err != nil {
// manejar el error
}
// Reembolsar una transacción
refund, err := tx.Refund("orden-de-compra", "597055555542", "orden-detalle-1234", 10000)
if err != nil {
// manejar el error
}
// Solo en ambientes con captura diferida
capture, err := tx.Capture("orden-de-compra", "597055555548", "codigo-de-autorizacion", 10000)
if err != nil {
// manejar el error
}import (
"github.com/ppastene/transbank-sdk-go"
"github.com/ppastene/transbank-sdk-go/transaccioncompleta"
)
opts := transbank.Options{
CommerceCode: "597055555530", // código de comercio Transacción Completa
ApiKey: "579B532A7440BB0C9079DED94D31EA1615BACEB56610332264630D42D0A36B1C",
Environment: transbank.Integration,
}
tx, err := transaccioncompleta.NewTransaction(opts)
if err != nil {
// manejar el error
}
// Crear una transacción con los datos de la tarjeta.
// El cvv es opcional (envíe "" si su comercio tiene la opción "sin cvv" habilitada).
create, err := tx.Create("orden-de-compra", "id-de-sesion", 10000, "4051885600446623", "22/10", "123")
if err != nil {
// manejar el error
}
_ = create.Token // token de la transacción
// Consultar las cuotas disponibles (opcional, solo si el pago es en cuotas)
installments, err := tx.Installments("token", 3)
if err != nil {
// manejar el error
}
_ = installments.DeferredPeriods // []DeferredPeriods, vacío si no hay periodos diferidos
// Confirmar la transacción.
// Los tres últimos parámetros son opcionales y solo se envían si el pago es en cuotas.
idQuery := 15
periodIndex := 1
gracePeriod := false
commit, err := tx.Commit("token", &idQuery, &periodIndex, &gracePeriod)
if err != nil {
// manejar el error
}
// Para pago en una sola cuota: tx.Commit("token", nil, nil, nil)
// Consultar el estado de una transacción
status, err := tx.Status("token")
if err != nil {
// manejar el error
}
_ = status.IsApproved()
// Reembolsar una transacción
refund, err := tx.Refund("token", 10000)
if err != nil {
// manejar el error
}
_ = refund.Type // "NULLIFY" o "REVERSED"
// Capturar una transacción (solo en ambientes con captura diferida)
capture, err := tx.Capture("token", "orden-de-compra", "codigo-de-autorizacion", 10000)
if err != nil {
// manejar el error
}import (
"github.com/ppastene/transbank-sdk-go"
"github.com/ppastene/transbank-sdk-go/transaccioncompleta"
)
opts := transbank.Options{
CommerceCode: "597055555551", // código de comercio Transacción Completa Mall
ApiKey: "579B532A7440BB0C9079DED94D31EA1615BACEB56610332264630D42D0A36B1C",
Environment: transbank.Integration,
}
mallTx, err := transaccioncompleta.NewMallTransaction(opts)
if err != nil {
// manejar el error
}
// Crear una transacción con los datos de la tarjeta.
// El cvv es opcional (envíe "" si su comercio tiene la opción "sin cvv" habilitada).
details := []transaccioncompleta.MallDetails{
{
Amount: 10000,
CommerceCode: "597055555552", // código de comercio de la tienda 1
BuyOrder: "orden-detalle-1234",
},
{
Amount: 20000,
CommerceCode: "597055555553", // código de comercio de la tienda 2
BuyOrder: "orden-detalle-4321",
},
}
create, err := mallTx.Create("orden-de-compra", "id-de-sesion", "4051885600446623", "22/10", details, "")
if err != nil {
// manejar el error
}
_ = create.Token // token de la transacción
// Consultar las cuotas disponibles por tienda (opcional, solo si el pago es en cuotas)
installmentsDetails := []transaccioncompleta.MallInstallmentsDetails{
{
CommerceCode: "597055555552",
BuyOrder: "orden-detalle-1234",
InstallmentsNumber: 3,
},
}
installments, err := mallTx.Installments("token", installmentsDetails)
if err != nil {
// manejar el error
}
_ = installments // []MallTransactionInstallmentsDetail, una entrada por tienda
// Confirmar la transacción.
// Los campos IdQueryInstallments, DeferredPeriodIndex y GracePeriod son opcionales
// y solo se envían si el pago es en cuotas (omítalos para pago en una sola cuota).
idQuery := 15
periodIndex := 1
commitDetails := []transaccioncompleta.MallCommitDetails{
{
CommerceCode: "597055555552",
BuyOrder: "orden-detalle-1234",
IdQueryInstallments: &idQuery,
DeferredPeriodIndex: &periodIndex,
},
}
commit, err := mallTx.Commit("token", commitDetails)
if err != nil {
// manejar el error
}
_ = commit.IsApproved() // true si todos los detalles fueron aprobados
// Consultar el estado de una transacción
status, err := mallTx.Status("token")
if err != nil {
// manejar el error
}
_ = status.IsApproved()
// Reembolsar una transacción (se indica la tienda y su orden)
refund, err := mallTx.Refund("token", "orden-detalle-1234", "597055555552", 10000)
if err != nil {
// manejar el error
}
_ = refund.Type // "NULLIFY" o "REVERSED"
// Capturar una transacción (solo en ambientes con captura diferida)
capture, err := mallTx.Capture("token", "597055555552", "orden-detalle-1234", "codigo-de-autorizacion", 10000)
if err != nil {
// manejar el error
}PatPass usa su propio paquete (patpass) y credenciales: el código de comercio y una llave de autorización, no la ApiKey de los demás servicios. El ambiente se configura con patpass.Integration o patpass.Production.
import (
"github.com/ppastene/transbank-sdk-go/patpass"
)
opts := patpass.Options{
CommerceCode: "28299257", // su código de comercio PatPass
Authorization: "cxxXQgGD9vrVe4M41FIt", // su llave de autorización
Environment: patpass.Integration,
}
ins, err := patpass.NewInscription(opts)
if err != nil {
// manejar el error
}
// Iniciar la inscripción del cliente
start, err := ins.Start(
"https://www.mi-tienda.cl/finalizar-suscripcion", // url
"Diego", // nombre
"Sanchez", // pApellido
"Valdovinos", // sApellido
"12345678-9", // rut
"323123", // serviceId
"https://www.mi-tienda.cl/voucher", // finalUrl
"", // montoMaximo (puede ir vacío)
"57508624", // telefonoFijo
"57508624", // telefonoCelular
"Help - 8050014", // nombrePatPass
"persona@test.cl", // correoPersona
"comercio@test.cl", // correoComercio
"Merced 156, Santiago, Chile", // direccion
"Santiago", // ciudad
)
if err != nil {
// manejar el error
}
_ = start.Token // token de la inscripción
_ = start.Url // URL del formulario de inscripción
// Consultar el estado de la inscripción con el token recibido en el retorno
status, err := ins.Status(start.Token)
if err != nil {
// manejar el error
}
_ = status.Authorized // true si la inscripción fue aprobada
_ = status.VoucherUrl // URL del voucherEl SDK devuelve errores tipados que reflejan dónde falló la operación, todos
discriminables con errors.As:
*transbank.ValidationError: parámetros o credenciales inválidas; la API no se llamó.*transbank.TransportError: no se pudo completar el request o procesar la respuesta (red, encoding, parsing).Erres la causa raíz.*transbank.HTTPError: la API de Transbank respondió con un código distinto de 2xx.StatusCodeyBody(respuesta cruda) permiten diagnosticar.
import (
"errors"
"fmt"
"github.com/ppastene/transbank-sdk-go"
)
_, err := tx.Status("token")
if err != nil {
var valErr *transbank.ValidationError
var tpErr *transbank.TransportError
var httpErr *transbank.HTTPError
switch {
case errors.As(err, &valErr):
fmt.Println("Error de validación del SDK:", valErr.Message)
case errors.As(err, &tpErr):
fmt.Println("Error de comunicación:", tpErr.Err)
case errors.As(err, &httpErr):
fmt.Printf("Error HTTP %d: %s\n", httpErr.StatusCode, httpErr.Body)
}
}El SDK se comunica con la API de Transbank a través de la interfaz transbank.HTTPClient (un método Do(req *http.Request) (*http.Response, error)), que el http.Client de la librería estándar satisface. Por defecto se usa un cliente interno con timeout de 30 segundos. Si necesita un comportamiento distinto (proxy, TLS, timeouts personalizados o un mock para tests), inyéctelo en Options.HTTPClient.
Con un http.Client de la librería estándar:
import (
"net/http"
"time"
)
client := &http.Client{
Timeout: 15 * time.Second,
Transport: &http.Transport{
Proxy: http.ProxyFromEnvironment,
},
}
opts := transbank.Options{
CommerceCode: "597055555532",
ApiKey: "579B532A7440BB0C9079DED94D31EA1615BACEB56610332264630D42D0A36B1C",
Environment: transbank.Integration,
HTTPClient: client,
}
tx, err := webpayplus.NewTransaction(opts)
if err != nil {
// manejar el error
}*http.Client ya implementa Do, así que se inyecta tal cual.
La interfaz transbank.HTTPClient solo pide un método: Do(req *http.Request) (*http.Response, error). Si usted tiene su propio cliente o usa uno externo como Resty, hay dos formas de adaptarlo.
1. Usar el *http.Client subyacente de la librería. Muchas librerías están construidas sobre net/http y exponen su cliente interno, que ya implementa Do. Resty lo expone con GetClient():
import (
"net/http"
"time"
"github.com/go-resty/resty/v2"
"github.com/ppastene/transbank-sdk-go"
)
restyClient := resty.New().SetTimeout(15 * time.Second)
opts := transbank.Options{
CommerceCode: "597055555532",
ApiKey: "579B532A7440BB0C9079DED94D31EA1615BACEB56610332264630D42D0A36B1C",
Environment: transbank.Integration,
HTTPClient: restyClient.GetClient(), // *http.Client subyacente de Resty
}2. Escribir un adaptador que implemente Do. Si la librería usa tipos propios, o quiere que los requests pasen por su pipeline (retries y middlewares de Resty), escriba un wrapper que traduzca *http.Request a los tipos de la librería y devuelva un *http.Response válido (con StatusCode y Body), que es lo único que el SDK consume:
import (
"bytes"
"io"
"net/http"
"time"
"github.com/go-resty/resty/v2"
"github.com/ppastene/transbank-sdk-go"
)
// AdaptadorResty envuelve un resty.Client para que cumpla con transbank.HTTPClient.
type AdaptadorResty struct {
cliente *resty.Client
}
func (a *AdaptadorResty) Do(req *http.Request) (*http.Response, error) {
body, err := io.ReadAll(req.Body)
if err != nil {
return nil, err
}
r := a.cliente.R().SetBody(body)
for name, values := range req.Header {
r.SetHeader(name, values[0])
}
rr, err := r.Execute(req.Method, req.URL.String())
if err != nil {
return nil, err
}
return &http.Response{
StatusCode: rr.StatusCode(),
Header: rr.Header(),
Body: io.NopCloser(bytes.NewReader(rr.Body())),
}, nil
}
restyClient := resty.New().
SetRetryCount(3).
SetRetryWaitTime(200 * time.Millisecond)
opts := transbank.Options{
CommerceCode: "597055555532",
ApiKey: "579B532A7440BB0C9079DED94D31EA1615BACEB56610332264630D42D0A36B1C",
Environment: transbank.Integration,
HTTPClient: &AdaptadorResty{cliente: restyClient},
}Si no se especifica, el SDK usa su cliente interno con timeout de 30 segundos.
Para ver un ejemplo completo de integración con cada servicio, consulte el repositorio transbank-sdk-go-example.